5

Consider this code

"Type: array"
$v = @()
"  -eq `$null: $($v -eq $null)"
"  -ne `$null: $($v -ne $null)"

"Type: string"
$v = ''
"  -eq `$null: $($v -eq $null)"
"  -ne `$null: $($v -ne $null)"

which produces the following result:

Type: array
  -eq $null: 
  -ne $null: 
Type: string
  -eq $null: False
  -ne $null: True
  1. Why does -eq and -ne behave as expected for an empty string, but evaluate to null or empty for an empty array?

  2. More importantly, how do you distinguish between $null and an empty array?

alx9r
  • 3,675
  • 4
  • 26
  • 55

2 Answers2

6

When comparison operators are used against a collection, they do not return a boolean value ($true or $false). They return all the members of the collection that meet the condition.

See:

Get-help About_Comparison_Operators

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
mjolinor
  • 66,130
  • 7
  • 114
  • 135
  • Oh. That makes a bit more sense. But then why doesn't `@() -eq $null` evaluate to true? – alx9r Apr 13 '15 at 20:20
  • @alx9r That answer might be dependent on your PowerShell version. In 3.0 that evaluates to False for me. – Matt Apr 13 '15 at 20:36
  • 1
    @() -eq $null will return all the members of the empty array that are equal to $null, so the result will be null, not $true. Note that $() -eq $null does return $true. $() evaluates to a scalar (single) null. – mjolinor Apr 13 '15 at 20:40
  • @mjolinor Can you tell me (or link to) what the meaning of `$()` is? Neither google nor stackoverflow search turns anything up for that character string. – alx9r Apr 13 '15 at 20:51
  • That's a subexpression. See get-help about_Operators. – mjolinor Apr 13 '15 at 20:53
2

@mjolinor's answer is a simple and correct answer to question (1). That answer actually leads to two more questions.

about_Comparison_Operators states the following:

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.

This implies the behavior depends on Powershell's definition of "collection" in this context. Pipeline unrolling has a similar dependence on the definition of "collection". It also implies that swapping the LHS and RHS of comparison operators might make a material difference.

So in addition to the original question 2, we have two more unanswered questions:

  1. How do you distinguish between $null and an empty array?
  2. What types does Powershell comparison operators treat as a collection?
  3. Does swapping the LHS and RHS of the comparison operators make a difference?

These three remaining questions can all be answered empirically using the script at the end of this answer.

Test Results

CollectionType x_eq_Null coll_LHS_result Null_eq_x coll_RHS_result
-------------- --------- --------------- --------- ---------------
string             False Boolean             False Boolean        
ArrayList             {} Object[]            False Boolean        
Hashtable          False Boolean             False Boolean        
Queue                 {} Object[]            False Boolean        
SortedList         False Boolean             False Boolean        
Stack                 {} Object[]            False Boolean        
Dictionary         False Boolean             False Boolean        
List                  {} Object[]            False Boolean        
Null                True Boolean              True Boolean        
boolean            False Boolean             False Boolean        
int32              False Boolean             False Boolean        
char               False Boolean             False Boolean        

We can use the table to answer the questions:

  1. How do you distinguish between $null and an empty array?

Moving the collection to the RHS of the operator (e.g. $null -eq @()) yields a Boolean for all types tested (see column coll_RHS_result). This means that $null -ne @() evaluates to True as expected which allows us to reliably detect the difference between $null and @().

  1. What types does Powershell comparison operators treat as a collection?

Each of the types whose coll_LHS_result is Object[].

  1. Does swapping the LHS and RHS of the comparison operators make a difference?

It sure does. Putting a collection on the LHS of the operator yields an array as a result. Putting a scalar on the LHS of the operator yields a boolean as a result.

The Test Script

('string',     ('')),
('ArrayList',  (New-Object System.Collections.ArrayList)),
('Hashtable',  (New-Object System.Collections.Hashtable)),
('Queue',      (New-Object System.Collections.Queue)),
('SortedList', (New-Object System.Collections.SortedList)),
('Stack',      (New-Object System.Collections.Stack)),
('Dictionary', (New-Object "System.Collections.Generic.Dictionary``2[System.String,int32]")),
('List',       (New-Object System.Collections.Generic.List``1[int32])),
('Null',       ($null)),
('boolean',    ($false)),
('int32',      ([int32]0)),
('char',       ([char]"`0")) |
    % {
        New-Object PSObject -Property @{ 
            CollectionType   =  $_[0]
            x_eq_Null        =  $_[1] -eq $null
            coll_LHS_result  = ($_[1] -eq $null).GetType().Name
            Null_eq_x        =  $null -eq $_[1]
            coll_RHS_result  = ($null -eq $_[1]).GetType().Name
        }
    } |
    select CollectionType,x_eq_Null,coll_LHS_result,Null_eq_x,coll_RHS_result |
    ft -AutoSize
Community
  • 1
  • 1
alx9r
  • 3,675
  • 4
  • 26
  • 55
  • And another interesting related question - how are objects in general, and collections specifically, converted/cast to boolean (true/false) values for evaluation of statements such as `if ($x)`? The answer lies in `System.Management.Automation.LanguagePrimitives.IsTrue` : http://blog.whatsupduck.net/2012/01/casting-objects-to-boolean-in.html – Ohad Schneider May 05 '16 at 18:43