Search code examples
arrayspowershellcount

PowerShell .Count returns 1 on empty array


If I use .Count to check the number of items in an empty array, like this:

$theNames = @()
$theTotalNames = $theNames.Count

it finds nothing, and the numeric variable $theTotalNames is 0, as expected

But I have a situation where if I use .Count to check the contents of seemingly empty array, it is returning 1.

I'm populating the array with the results returned from the Invoke-RestMethod query like this:

$responseData = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
$theNames = @($responseData.PsObject.Properties["the_field"].value.field_name)
$theTotalNames = $theNames.Count

If the query returns nothing because there were no fields found, $theTotalNames somehow equals 1. If the query returns one or more items, $theTotalItems will correctly equal 1.. or higher

When I display the contents of $theNames array after the query that returned nothing, the array seems empty.

If I check what's in the array, like this:

 if ($theNames) {
     "The array contains something"
 }
 else {
     "The array contains nothing"
 }

the console always says the array contains nothing.

So, why does .Count think there's at lease one item in the array?


Solution

  • As PetSerAl implies in a comment, the array may not be empty but may have a single element that happens to be $null; the .Count property reports the number of elements irrespective of the value of the elements:

    @($null).Count # -> 1
    

    Work around the problem as follows (assuming actual values are never the empty string):

    $theTotalNames = if ($theNames) { $theNames.Count } else { 0 }
    

    This relies on the fact that a single-element array that contains a "falsy" value is regarded as $False in a Boolean context.


    Optional background information

    In PowerShell, $null is generally a "something" (the "null scalar"), whereas there is also a "null collection", which is closer to "nothing", namely the [System.Management.Automation.Internal.AutomationNull]::Value singleton, which is "returned" by commands that have no output at all.

    The simplest way to produce it is to call an empty script block: & {}

    Trying to wrap that in an array indeed yields an empty array:

    @(& {}).Count  # -> 0
    

    However, in PSv3+ there is (at least?) one context in which $null too is considered "nothing":

    foreach ($el in $null) { "loop entered" }  # loop is NOT entered.
    

    I presume that the rationale for this intentional inconsistency is that uninitialized variables default to $null and that entering the loop for uninitialized variables would be undesirable.
    For more, see this GitHub discussion.