Given that this works:
$ar = @()
$ar -is [Array]
True
Why doesn't this work?
function test {
$arr = @()
return $arr
}
$ar = test
$ar -is [Array]
False
That is, why isn't an empty array returned from the test function?
Your function doesn't work because PowerShell returns all non-captured stream output, not just the argument of the return statement. An empty array is mangled into $null in the process. However, you can preserve an array on return by prepending it with the array construction operator (,):
function test {
$arr = @()
return ,$arr
}
$arr.add(1) which was outputting the count of the array after. Doing $x = $arr.add(1) fixed the issue. Hacky.... | Out-Null or ... >$null to suppress undesired output. I wouldn't consider returning all non-captured output a bad idea per se, though, it's just unexpected for most people with a background in other programming languages. When used properly it can actually simplify returning data from a function, because you don't have to collect all desired output in a variable before returning it.return $x is just shorthand for Write-Output $x; return. Clearly the keyword should be ExitSub and it shouldn't accept an argument.write-output $arr is as afaik the same as just writing $arr.
So the function will still return $null.
But write-output has the option -NoEnumerate.
That means the empty Array will not be enumerated
(and therefore ignored - because it's empty). The result is an empty array.
admittedly the above answer is much shorter, ...
function test {
$arr = @()
write-output $arr -NoEnumerate
}
(test) -is [array] ## $True
Write-Output. So Write-Output @() -NoEnumerate works as expected but @() | Write-Output -NoEnumerate does not. I hate PowerShell sometimes.Another thing to keep in mind with the "prepend ','" solution, is that if you intend to serialize the data afterwards, you're going to run into some issues. What , appears to actually do is wrap whatever variable it's prepending into an array. so $null becomes [], "test" becomes ["test"], but...... ["foo","bar"] becomes [["foo","bar"]], which is obviously an issue from a serialization standpoint.
, in its unaltered form. The examples you cited are what happens to the value after the , operator, but prior to being returned.Expanding on the answer by @Ansgar Wiechers ...
I learned yesterday that, in addition to the peculiarity with an empty array,:
If a function returns an array with one item, then the calling function sees the contained item in the array, not an array of one item.
If a function returns an array with two or more items, then the calling function sees the returned object as an array.
Sample code:
function New-Array {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)][int]$size
)
$arr = @()
for ($i = 0; $i -lt $size; $i++) {
$arr += $i*2
}
return $arr
}
$arr1 = New-Array 0
$arr2 = New-Array 1
$arr3 = New-Array 2
$arr4 = New-Array 5
if ($arr1 -eq $null) {
Write-Host "arr1 is null"
} else {
$objectTYpe = $arr1.GetType().FullName
Write-Host "arr1 type: $objectType"
}
if ($arr2 -eq $null) {
Write-Host "arr2 is null"
} else {
$objectTYpe = $arr2.GetType().FullName
Write-Host "arr2 type: $objectType"
}
if ($arr3 -eq $null) {
Write-Host "arr3 is null"
} else {
$objectTYpe = $arr3.GetType().FullName
Write-Host "arr3 type: $objectType"
}
if ($arr4 -eq $null) {
Write-Host "arr4 is null"
} else {
$objectTYpe = $arr4.GetType().FullName
Write-Host "arr4 type: $objectType"
}
Output of running the above script:
arr1 is null
arr2 type: System.Int32
arr3 type: System.Object[]
arr4 type: System.Object[]
In order for the calling function to see an array, regardless of the size of the array, the return statement needs to be tweaked for arrays of size less than 2.
if ($arr.Count -lt 2) {
return ,$arr # As suggested in the aceepted answer
} else {
return $arr
}
After that, all returned values are seen as arrays in the calling function.
arr1 type: System.Object[]
arr2 type: System.Object[]
arr3 type: System.Object[]
arr4 type: System.Object[]