概要
PowerShell の配列の要素を削除する際、「要素に $null
を書き込む」と解説してあるサイトをよく見かける。ある意味において間違いではないが、「配列要素の切り詰め=対象に $null
代入」と記憶していると処理後の配列について直感と異なる結果が返るので記録しておく。
環境
PS> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.15063.502
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.15063.502
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
実験
適当な配列を与え、その 0 番目の要素に $null
を書いてみる。
PS> $array = (1..10)
PS> foreach ($i in $array) { $a += $i }
PS> $a
55
PS> $array.Length
10
PS> $array[0] = $null
PS> $a = 0
PS> foreach ($i in $array) { $a += $i }
54
一見すると 0 番目の要素であった 1 が「なくなった」ように見える。
しかし、
PS> $array.Length
10 # 「 9 じゃないの!?」
PS> $array[1]
2 # 「 3 じゃないの!?」
「要素を削除した」にもかかわらず、次のようになっていることがわかる。
- 配列の長さが変わらない
- 配列の要素が詰まって(シフトして)いない
何が起きているのか
その答えはこちら1 に書いてあった。
+=演算子
で文字列の追加を行う場合に、PowerShell の@()
によるArray
は固定長なので配列をいちいち作り直します。
PowerShell の配列は固定長である。したがって配列の長さを変えられないのが仕様であった。
ではさっきのテストコードにおいて、 $array[0]
はどうなっているのだろう。
PS> $array[0] -eq $null
True
当たり前だが、自ら行った操作通り $null
であった。したがって $array
は $null
を先頭に残りが 2 から 10 の整数が格納された、長さ 10 の配列であることがわかる。
当初のテストコードのように、 $null
が要素に含まれる配列をイテレータとして利用する分には問題ない。一方で、 Length や Count で配列の長さを取得し、条件分岐等に使うケースでは問題になり得る。
$null
要素を取り除く
フィルターとしての -ne
を使って、次のように実現できる。
PS> $newarray = $array -ne $null
PS> $newarray.Length
9
結論
-
@()
で定義される Array は固定長である - 配列要素に
$null
を書き込むと:- イテレータとしては直感通り動作する
- 長さが問題になるケースでは注意が必要
補足
参考文献1 にあるとおり、 Array の要素を増減させるには配列を作り直して再度代入する必要があるため、計算コストが高い。配列の要素が頻繁に増減する環境では、代わりに(固定長ではない) ArrayList や List を使う方が計算コストを低くできる。