David Bakkers
David Bakkers

Reputation: 555

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?

Upvotes: 1

Views: 6780

Answers (2)

seagull
seagull

Reputation: 217

In my instance, I had already defined the $array variable, so mklement0's answer (checking the value of if $array) wouldn't have worked as the variable is always defined, just not always populated, despite what .count says.

I worked around it in the end by doing this:

if (($array | select -first 1) -eq $null) {
    write-host "Array is empty"
}

In short, we get the first result from the array and see whether it is $null. In my case this works because we never expect any of the elements in the array to be $null.

If you are in a situation where array elements can be $null, one option you might have is to remove these entries beforehand using where-object, like so; you could then perform the original .count check as all zero entries will have been removed.

$array2=$array | ? {$_ -ne $null}

Upvotes: 0

mklement0
mklement0

Reputation: 440317

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.

Upvotes: 2

Related Questions