1

I have a Powershell function that returns an array of hashtables. If the function returns a single entry in the array, then I get some odd behaviour, is this expected?

For context:

  • I'm a C# developer, so I'm expecting the return type to be consistent, regardless of the quantity of results :-)
  • Powershell Version: 5.1 on Windows 11
function GetArrayOfHashtable
{
    param([string] $name, [int]$count)
    $result = @()
    for ($i = 0; $i -lt $count; $i++) {
        $h = @{
            name = $name
            id=$i+1
        }
        $result += $h
    }
    return $result
}

$one = GetArrayOfHashtable "one" 1
$two = GetArrayOfHashtable "two" 2
$three = GetArrayOfHashtable "three" 3

Write-Host $one.gettype()     # 'System.Collections.Hashtable'  <--???
Write-Host $two.gettype()     # 'System.Object[]'
Write-Host $three.gettype()   # 'System.Object[]'

Write-Host $three[0].name  $three[0].id  # 'three 1'
Write-Host $two[0].name  $two[0].id      # 'two 1'
Write-Host $one[0].name  $one[0].id      # '' 

Update (for completeness) I can work around the issue by encapsulating the array of hashtables in another hashtable:

function GetArrayOfHashtable
{
    param([string] $name, [int]$count)
    $result = @()
    for ($i = 0; $i -lt $count; $i++) {
        $h = @{
            name = $name
            id=$i+1
        }
        $result += $h
    }
    return @{data =$result}  # NOTE: solution is to hold the result in a hashtable
}

$one = GetArrayOfHashtable "one" 1
$two = GetArrayOfHashtable "two" 2
$three = GetArrayOfHashtable "three" 3

Write-Host $one.data.gettype()     # 'System.Object[]'
Write-Host $two.data.gettype()     # 'System.Object[]'
Write-Host $three.data.gettype()   # 'System.Object[]'

Write-Host $three.data[0].name  $three.data[0].id  # 'three 1'
Write-Host $two.data[0].name  $two.data[0].id      # 'two 1'
Write-Host $one.data[0].name  $one.data[0].id      # 'one 1'
4
  • 1
    That's expected behaviour. If you have an array with only one element in it it will show the type of this one element. If there are more than one you see a list type. You could cast the result explicitly to an array if that helps. ... [array]$one = GetArrayOfHashtable "one" 1 Commented Dec 6, 2024 at 11:18
  • 1
    Yeah, same is true with basic arrays. Assign two items to it then PS will make it an array, one item it'll be a string (or whatever). If there's a chance it may only get one item, it's always worth explicitely setting the type, either when you assign it as you did with your work around and Olaf suggested, or initialise them before hand with $one=$() or $one=${} as appropriate. Commented Dec 6, 2024 at 11:31
  • 1
    You can achieve this but its going against the language. PowerShell functions return type is like an IEnumerable<object>. Commented Dec 6, 2024 at 12:15
  • 1
    In short: PowerShell enumerates arrays (and similar data structures) that you output from a function (whether or not you output them implicitly or via a return statement), i.e. it streams (outputs) their elements one by one. To output an array as a whole, use , $array (sic; or return , $array) or Write-Output -NoEnumerate $array. That said, this technique is best avoided in public functions, because the general expectation is element-by-element output. The alternative is to make the caller use @(...) to ensure receiving an array. See the linked duplicate for details. Commented Dec 6, 2024 at 13:10

1 Answer 1

0

As per the comments, this looks to be expected behaviour as described here

One solution is to use the unary operator:

function GetArrayOfHashtable
{
    param([string] $name, [int]$count)
    $result = @()
    for ($i = 0; $i -lt $count; $i++) {
        $h = @{
            name = $name
            id=$i+1
        }
        $result += $h
    }
    return ,$result  # <---  See comma here
}

or change the call to use @( ... ):

$one = @(GetArrayOfHashtable "one" 1)
Sign up to request clarification or add additional context in comments.

3 Comments

return ,$result should do the trick. However, I'd discourage from using the other suggested method as this returns always an array of count 1 regardless of the -count parameter…
"... as this returns always an array of count 1 regardless of the -count parameter…", that should be the case: function Test { $result = @{ a = 1 }, @{ b = 2}; return ,$result } --> (Test).Count --> 2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.