PowerShellで条件の真偽値に応じて変数へ格納する値を変える関数（VBAのIIF関数に相当）を作成

PowerShellで条件の真偽値に応じて変数へ格納する値を変える関数（VBAのIIF関数に相当）を作成してみました。

今回ご紹介する関数を定義すると、以下のような書き方が可能となります。

```ps1:PowerShell
# 注意：別途iif関数の定義（後述）が必要
$str = 'ABC'
$ret = (iif {$str.Contains('A')} -IfTrue 'Aが含まれている' -IfFalse 'Aが含まれていない') + `
       (iif {$str.Contains('B')} -IfTrue 'Bが含まれている' -IfFalse 'Bが含まれていない')

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```

## 問題点

PowerShellで条件の真偽値に応じて変数へ格納する値を変えたい場合、IF文を使って以下のような書き方ができます。

```ps1:PowerShell
$str = 'ABC'
if($str.Contains('A')){
  $ret = 'Aが含まれている'
} else {
  $ret = 'Aが含まれていない'
}

# 確認コマンド
echo ($ret -eq 'Aが含まれている')
echo $ret
<# 実行結果
True
Aが含まれている
#>
```

ちなみに、以下のような書き方もできます。

```ps1:PowerShell
$str = 'ABC'
$ret = if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'}

# 確認コマンド
echo ($ret -eq 'Aが含まれている')
echo $ret
<# 実行結果
True
Aが含まれている
#>
```

ここまでは何も問題がありません。しかし、以下のような書き方はできません。

```ps1:PowerShell
$str = 'ABC'
$ret = if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'} + `
       if($str.Contains('B')){'Bが含まれている'} else {'Bが含まれていない'}
<# エラー
発生場所 行:2 文字:63
+ $ret = if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'} + `
+                                                               ~
単項演算子 '+' の後に式が存在しません。
発生場所 行:3 文字:8
+        if($str.Contains('B')){'Bが含まれている'} else {'Bが含まれていない'}
+        ~~
式またはステートメントのトークン 'if' を使用できません。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingExpressionAfterOperator
#>
```

```ps1:PowerShell
$str = 'ABC'
$ret = (if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'}) + ` 
       (if($str.Contains('B')){'Bが含まれている'} else {'Bが含まれていない'})
<# エラー
if : 用語 'if' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから
、再試行してください。
発生場所 行:2 文字:9
+ $ret = (if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'}) + `
+         ~~
    + CategoryInfo          : ObjectNotFound: (if:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
#>
```

複数のIF文を式の結果として繋げるためには、以下のような書き方をする必要があります。
ただし、正直言ってあまり読みやすくないと思います。（慣れれば良いのと言われればそれまでなのかもしれませんが。）

```ps1:PowerShell
$str = 'ABC'
$ret = (& {if($str.Contains('A')){'Aが含まれている'} else {'Aが含まれていない'}}) + `
       (& {if($str.Contains('B')){'Bが含まれている'} else {'Bが含まれていない'}})

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```

## 解決策
以下の関数を定義します。ちなみに、関数名はVBAのIIf関数を参考としました。

```ps1:PowerShell
function IIf($Condition,$IfTrue,$IfFalse){
  if($Condition){$IfTrue} else {$IfFalse}
}
```

```ps1:使い方
$str = 'ABC'
$ret = (iif ($str.Contains('A')) -IfTrue 'Aが含まれている' -IfFalse 'Aが含まれていない') + `
       (iif ($str.Contains('B')) -IfTrue 'Bが含まれている' -IfFalse 'Bが含まれていない')

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```

多少はマシになったでしょうか。
もう少しPowerShellっぽくアレンジして、パイプラインで繋げられるようにすると以下のようになりました。

```ps1:PowerShell
filter Inline-If([ScriptBlock]$Condition,$IfTrue,$IfFalse){
  if(& $Condition $_){$IfTrue} else {$IfFalse}
}
Set-Alias iif Inline-If
```

```ps1:使い方
$str = 'ABC'
$ret = (iif {$str.Contains('A')} -IfTrue 'Aが含まれている' -IfFalse 'Aが含まれていない') + `
       (iif {$str.Contains('B')} -IfTrue 'Bが含まれている' -IfFalse 'Bが含まれていない')

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>

$str = 'ABC'
$str = 'ABC'
$ret = ($str | iif {([string]$args).Contains('A')} -IfTrue 'Aが含まれている' -IfFalse 'Aが含まれていない') + `
       ($str | iif {([string]$args).Contains('B')} -IfTrue 'Bが含まれている' -IfFalse 'Bが含まれていない')

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```

## まとめ
PowerShellはパイプラインを使って処理をがしがし繋いでいくのが気持ちいい言語なので、処理の結果を直接式に使用できることが重要だと思っています。

## （追記）番外編
かなり好みが分かれると思いますが、以下のような書き方もできます。
ただし、条件に一致しない場合の値を先に記述しなければいけないので、読みにくいのではないかと思っています。

```ps1:PowerShell
$str = 'ABC'
$ret = @('Aが含まれていない','Aが含まれている')[$str.Contains('A')] + `
       @('Bが含まれていない','Bが含まれている')[$str.Contains('B')]

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```

やや冗長にはなりますが、以下のような書き方の方がまだ読みやすいと思います。

```ps1:PowerShell
$str = 'ABC'
$ret = @{$true='Aが含まれている';$false='Aが含まれていない'}[$str.Contains('A')] + `
       @{$true='Bが含まれている';$false='Bが含まれていない'}[$str.Contains('B')]

# 確認コマンド
echo ($ret -eq 'Aが含まれているBが含まれている')
echo $ret
<# 実行結果
True
Aが含まれているBが含まれている
#>
```