2019年3月19日火曜日

PowerShellでとても大切な「連想配列(ハッシュテーブル)」について

PowerShellは、もはやWindowsだけのものではないオープンソースに進化していますが、何はともあれ、まずはWindowsユーザー(7以降)なら100%全員が今すぐに使える環境として他には無い絶大メリットを持っています。

そんなPowerShellですが、今回は「連想配列(ハッシュテーブル)」について書かせて頂きたいと思います。


■連想配列とは?

普通の配列は、@(1,2,3,4,5)、あるいは@(1..5)のように単純に値を何個も連続して「要素(=値)」を格納している配列オブジェクトです。

対する連想配列は、要素に名前(キー名)がつけられる配列オブジェクトです。

$変数=@{"名前";"値"}という具合に定義しておくと、$変数["名前"]や、$変数.名前 という書き方で値を取得する事が可能です。

また、値の部分には何でも格納出来るので、たとえば、配列をまとめて格納しておく入れ物としても使えて、応用範囲がとても広がります。

さらに、SORTの昇順・降順混在のように、パラメーターを連想配列でセットするコマンドレットも存在するため、パッと使えるようにしておくと良い感じです。


■連想配列を使ってみる

とりあえず、Windowsフォルダの直下にあるファイル/フォルダの一覧を連想配列に格納してみたいと思います。

ファイル/フォルダ名を名前(キー)にして、値には、配列の形式でもって、長さ[0],最終更新日[1], フォルダかどうか($True,$False)[2]を格納してみます。




#連想配列の定義
$V = @{}

#連想配列に値のセット
GET-CHILDITEM C:\WINDOWS | %{$V.Add($_.Name,@($_.Length,$_.LastWriteTime.ToString("yyyyMMdd hhmmss"),$_.PSIsContainer))}

#連想配列からの値の取り出し(1. foreachステートメント)
foreach($I in $V.Keys) { WRITE-HOST (($I).PadRight(30) + " 最終更新日時=" + $V[$I][1])}

#連想配列からの値の取り出し(2.foreach-objectコマンドレットによる値の取り出し)
$V.GetEnumerator() | %{ WRITE-HOST $_.Key.PadRight(30) $_.Value[1]}



↑連想配列は@{}で定義出来て、Addメソッドでもって、どんどんと名前,値を追加して行く事が出来ます。

ポイントは連想配列を作るのは良いけど、取り出すのはどうするのか??という話です。

まずは、プログラミングライクに、foreachステートメントで取り出してみます。

foreach(取り出したキー in 連想配列.Keys) { 連想配列[取り出したキー]... }という書き方です。

単純に$連想配列 | %{$_.Key}という書き方は出来ない(1件づつ値を取る事が出来ない=出来るようにする方法は後述)ので注意が必要です。


■ソートはどうやるの?
foreach-objectコマンドで値を取り出したい


ところが、foreachステートメントだと、ソートなどが大変になります。

そもそもコマンドレットを使いたい!!

そんな時は、$連想配列.GetEnumerator()メソッドを使います。

$連想配列.GetEnumerator()メソッドを使うとコマンドレットで一件づつの取り出しやソートが可能になります



#連想配列の定義
$V = @{}

#連想配列に値のセット
GET-CHILDITEM C:\WINDOWS | %{$V.Add($_.Name,@($_.Length,$_.LastWriteTime.ToString("yyyyMMdd hhmmss"),$_.PSIsContainer))}

#連想配列からの値の取り出し(2.foreach-objectコマンドレットによる値の取り出し)
$V.GetEnumerator() | %{ WRITE-HOST $_.Key.PadRight(30) $_.Value[1]}

#さらにGetEnumerator()メソッドはソートにも対応
$V.GetEnumerator() | SORT @{Expression={$_.Value[1]};Descending=$True},@{Expression={$_.Name}} | %{ WRITE-HOST $_.Key.PadRight(30) $_.Value[1]}

↑GetEnumerator()メソッドを使った後だと、パイプラインで次のコマンドレットにチェーンし一件づつ取り出す事が可能です。

平たく言うと、GetEnumeratorは「オブジェクトを列挙可能な状態にする」というような感じです。

パイプラインでチェーンが可能になると、SORT-OBJECT(SORT)コマンドレットで最終更新日時の降順、名前の昇順でソートし、foreach-object(%{})コマンドレットに送り込めて平易な書き方で一件づつ取り出して処理出来るようになります。