Vincent
Vincent

Reputation: 203

PowerShell empty array initialization fails in conditional variable assignment

Following this post about how to best create a (ternary operator)-ish expression in PowerShell, I've tried to convert this:

if ($condition)
{ 
   $accessList = @() 
}
else
{
    $accessList = $accessControl.AllowedItems
}

into this:

$accessList = if ($condition) { @() } else { $accessControl.AllowedItems }

The one-liner fails to initialize an empty array. The resulting $accessList doesn't have any array properties/methods and $NULL -eq $accessList returns True. This version doesn't work either:

$accessList = $(if ($condition) { @() } else { $accessControl.AllowedItems })

Oddly enough, if I attempt to initialize a non-empty array, the one-liner works. It probably has something to do with variable evaluation and how the pipeline works, but after googling for a bit, I've found no detailed explanation of this particluar case. Please help understand the mechanics behind it.

Upvotes: 1

Views: 730

Answers (1)

mklement0
mklement0

Reputation: 439193

You need to prevent the implicit enumeration of your empty array in the (implicitly used) pipeline, the simplest solution to which is to wrap it in an aux., transitory array, which you can construct with the unary form of ,, the array constructor operator:

$accessList = if ($condition) { , @() } else { $accessControl.AllowedItems }

PowerShell 7+ offers a way to bypass the problem, by using ?:, the ternary conditional operator, which acts as an expression (see explanation below):

$accessList = $condition ? @() : $accessControl.AllowedItems 

Background information:

The distinction is subtle:

In:

$accessList = @()

@() is an expression and therefore assigned as-is.

In:

$accessList = if ($condition) { @() }

if ($condition) { @() } is an entire statement, and as such its output (@()) implicitly uses the pipeline, where an array is implicitly enumerated and its elements are sent to the pipeline; in the case of an empty array, there is nothing to enumerate, and therefore nothing is output ( which is technically represented as the [System.Management.Automation.Internal.AutomationNull]::Value singleton).

Wrapping the array in an aux., transitory array as shown at the top makes the pipeline enumerate only that aux. array, so the wrapped array is output as a whole.

The same applies to outputs from commands, notably if you try to output an array as a whole from a script or function.

Generally, though, note that PowerShell commands are not expected to output collections as a whole and to instead enumerate them.

Also note that if two or more elements are enumerated in the pipeline and you assign the output to a variable, PowerShell implicitly collects them in an [object[]] array, whereas a single element is assigned as-is (no array wrapper). See this answer for more information.

Upvotes: 5

Related Questions