Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an empty Powershell hash table evaluate to True when an empty array evaluates to False?

I'm experimenting with True and False in Powershell 4.0. Here are the results when looking at an empty array and an empty hash table:

PS C:\Users\simon> $a = @()    
PS C:\Users\simon> if ($a) { Write-Host "True" } else { Write-Host "False" }
False

PS C:\Users\simon> $a = @{}    
PS C:\Users\simon> if ($a) { Write-Host "True" } else { Write-Host "False" }
True

Why do empty hash tables evaluate to True when empty arrays evaluate to False?

According to the Powershell documentation About Comparison Operators:

When the input is a collection of values, the comparison operators return any matching values. If there are no matches in a collection, comparison operators do not return anything.

From that I would expect both a hash table and an array to act the same when they are empty, since both are collections. I would expect both to evaluate to False because they return nothing to the if clause.

like image 938
Simon Tewsi Avatar asked Nov 06 '17 02:11

Simon Tewsi


People also ask

What are differences between an array and hash in PowerShell?

An array, which is sometimes referred to as a collection, stores a list of items. A hash table, which is sometimes called a dictionary or an associative array, stores a paired list of items.

How do I create an empty Hashtable in PowerShell?

An empty PowerShell hash table is created using @{}. PowerShell hash table values are added using $table. Add(key, value) syntax or $table. key = value syntax.


1 Answers

This isn't a question of True/False. You can test this using the Boolean operators $true and $false. I've used $h as the empty hashtable @{}

PS C:\> $a = @()
PS C:\> $h = @{}
PS C:\> if ($a -eq $true) { Write-Host "True" } else { Write-Host "False" }
False

if ($h -eq $true)    >  False
if ($a -eq $false)   >  False
if ($h -eq $false)   >  False

Also not equal to automatic variable $null:

if($a -eq $null)     >  False
if($h -eq $null)     >  False

As per iRon's link, Count is a better test to see if a hashtable/array is empty. Related Length

The different behaviour will be related to the fundamental types.

PS C:\> $a.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

PS C:\> $h.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

It's to do with how the if statement works and the Length property, AFAICS. Below is my (admittedly shaky) understanding from multiple StackOverflow posts and external links.


Get-Member behaviour is different - see Mathias's explanation

$a | Get-Member            # Error - Get-Member : You must specify an object for the Get-Member cmdlet.
$h | Get-Member            # TypeName: System.Collections.Hashtable. Has Count method, no Length property
Get-Member -InputObject $a # TypeName: System.Object[]. Has Count method, has Length property

The Length property of the variables is different. But the hashtable does not have a Length property - see mjolinar's answer.

$a.Length   > 0
$h.length   > 1

Combining with Ansgar's link explains the different behaviors in the if statement as it seems to be implicitly getting the Length property . It also allows us to do this:

if ($a.Length -eq $true)    >  False
if ($h.Length -eq $true)    >  True

Using the IsNullOrEmpty method of the [String] .Net class gives different output compared to $null, I assume because this also relies on the Length.

if ([string]::IsNullOrEmpty($a))    > True
if ([string]::IsNullOrEmpty($h))    > False
like image 163
G42 Avatar answered Oct 03 '22 15:10

G42