Reputation: 415
I have the following, which works but looks clunky:
if($config.contentDir){
$contentDir = $config.contentDir
} else {
$contentDir = "contents"
}
Is there a nicer way of doing this? I have seen this answer here, but it isn't exactly "nicer". Just wondering if 6.0 brought any improvements?
I'm likely to be handling a large amount of config options, so it's going to get fairly messy.
Upvotes: 3
Views: 1922
Reputation: 437100
Update:
Null-coalescing operators were introduced in PowerShell (Core) 7.0 (along with a ternary operator), which enables the following v7+ solution:
$contentDir = $config.contentDir ?? 'content'
PowerShell v6- solutions:
What you'e looking for is null-coalescing, which PowerShell doesn't have as of v7.0.0-preview.4.
For now, this will have to do:
$contentDir = if ($null -eq $config.contentDir) { 'content' } else { $config.contentDir }
Note: $null
is deliberately placed on the LHS of -eq
to unambiguously test for $null
, because as the RHS it would act as a filter if the value to test happens to be array-valued.
An adaptation of Lee Daily's array-based answer enables a more concise solution:
$contentDir = ($config.ContentDir, 'content')[$null -eq $config.ContentDir]
Use of the ternary operator (conditional), which will be implemented in v7.0, enables a similarly concise equivalent:
$contentDir = $null -eq $config.contentDir ? 'content' : $config.contentDir
However, all these approaches have the following undesirable aspects:
They require an explicit reference to $null
; note that if ($config.ContentDir)
- i.e. coercing the value to a Boolean - may work with strings, but is not generally robust, because non-$null
values such as 0
can evaluate to $false
too.
$config.contentDir
, the value to test for $null
, must be accessed twice, which can have side effects.
Defining a custom function named, say, ??
, can address these problems:
# Custom function that emulates null-coalescing.
function ?? ($PossiblyNull, $ValueIfNull) {
if ($null -eq $PossiblyNull) { $ValueIfNull } else { $PossiblyNull }
}
$contentDir = ?? $config.contentDir 'content'
However, such a custom function has down-sides:
The down-sides of custom functions are:
You need to include or import them into in every piece of code you want to use them in.
If you choose familiar name such as ??
, the placement of operands can get confusing, because you must (invariably) place them differently in PowerShell, given the implementation as a function (e.g., a ?? b
in C# vs. ?? $a $b
in PowerShell) - especially once true null-coalescing gets implemented in PowerShell: see next section.
And, of course, calling a function adds overhead.
If this GitHub feature request is implemented, you'll be able to use true null-coalescing, which is both the most concise solution and avoids the aforementioned undesirable aspects:
# Hopefully soon
$contentDir = $config.contentDir ?? 'content'
A related feature also proposed in the linked GitHub issue is null-conditional assignment, $config.ContentDir ?= 'content'
Upvotes: 3
Reputation: 24525
This is a little shorter...
$contentDir = if ( $config.contentDir ) { $config.contentDir } else { "contents" }
You could also define an iif
function:
function iif {
param(
[ScriptBlock] $testExpr,
[ScriptBlock] $trueExpr,
[ScriptBlock] $falseExpr
)
if ( & $testExpr ) {
& $trueExpr
}
else {
& $falseExpr
}
}
Then you could shorten to this:
$contentDir = iif { $config.contentDir } { $config.contentDir } { "contents" }
As an aside, it looks like the next version of PowerShell will support the ternary operator (see https://devblogs.microsoft.com/powershell/powershell-7-preview-4/), so in the future, you'll be able to write something like:
$contentDir = $config.contentDir ? $config.contentDir : "contents"
Upvotes: 4
Reputation: 27428
Sort of like '||' in bash. If the first one is false or null, it will do the second one.
[void](($contentDir = $config.contentDir) -or ($contentDir = "contents"))
Upvotes: 0
Reputation: 7479
as Bill_Stewart
showed, there is a ternary operator due in ps7. however, you can get something similar by using a two-item array and taking advantage of how PoSh will coerce values -- $False
gives 0
, $True
gives 1
.
$Config = [PSCustomObject]@{
ContentDir = 'SomewhereElse'
}
#$Config.ContentDir = ''
$ContentDir = @('contents', $Config.ContentDir)[[bool]$Config.ContentDir]
$ContentDir
output with line 4 commented out = SomewhereElse
output with line 4 enabled = contents
Upvotes: 2