ハッシュをループする、またはPowerShellで配列を使用する


96

私はこの(簡略化された)コードチャンクを使用して、SQL ServerからBCPでテーブルのセットを抽出しています。

$OutputDirectory = "c:\junk\"
$ServerOption =   "-SServerName"
$TargetDatabase = "Content.dbo."

$ExtractTables = @(
    "Page"
    , "ChecklistItemCategory"
    , "ChecklistItem"
)

for ($i=0; $i -le $ExtractTables.Length – 1; $i++)  {
    $InputFullTableName = "$TargetDatabase$($ExtractTables[$i])"
    $OutputFullFileName = "$OutputDirectory$($ExtractTables[$i])"
    bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
}

これはうまく機能しますが、現在、ビューを介して抽出する必要があるテーブルとそうでないテーブルがあります。だから私はこのようなデータ構造が必要です:

"Page"                      "vExtractPage"
, "ChecklistItemCategory"   "ChecklistItemCategory"
, "ChecklistItem"           "vExtractChecklistItem"

私はハッシュを見ていましたが、ハッシュをループする方法について何も見つかりませんでした。ここで正しいことは何でしょうか?おそらく配列だけを使用しますが、両方の値をスペースで区切って使用しますか?

または私は明白な何かを逃していますか?

回答:


108

クリスチャンの答えはうまく機能し、GetEnumeratorメソッドを使用して各ハッシュテーブルアイテムをループする方法を示しています。keysプロパティを使用してループすることもできます。以下に例を示します。

$hash = @{
    a = 1
    b = 2
    c = 3
}
$hash.Keys | % { "key = $_ , value = " + $hash.Item($_) }

出力:

key = c , value = 3
key = a , value = 1
key = b , value = 2

ハッシュを別の順序で列挙するにはどうすればよいですか?たとえば、コンテンツを昇順で印刷したい場合(ここではa ... b ... c)。それも可能ですか?
LPrc

5
なぜ$hash.Item($_)代わりに$hash[$_]
alvarez

1
@LPrcは、Sort-Objectメソッドを使用してこれを行います。この記事はそれについてかなり良い説明をしています:technet.microsoft.com/en-us/library/ee692803.aspx
chazbot7

4
あなただけでも使用できます$hash.$_
TNT

1
ハッシュテーブルにkey = 'Keys'が含まれている場合、このアプローチは機能しません。
Boris Nikitin

195

スクリプトでは省略形は推奨されません。読みにくくなります。%{}演算子は省略形と見なされます。可読性と再利用性のためにスクリプトでどのように実行する必要があるかを次に示します。

変数の設定

PS> $hash = @{
    a = 1
    b = 2
    c = 3
}
PS> $hash

Name                           Value
----                           -----
c                              3
b                              2
a                              1

オプション1:GetEnumerator()

注:個人的な好み。構文が読みやすい

GetEnumerator()メソッドは、次のように実行されます。

foreach ($h in $hash.GetEnumerator()) {
    Write-Host "$($h.Name): $($h.Value)"
}

出力:

c: 3
b: 2
a: 1

オプション2:キー

Keysメソッドは次のように実行されます。

foreach ($h in $hash.Keys) {
    Write-Host "${h}: $($hash.Item($h))"
}

出力:

c: 3
b: 2
a: 1

追加情報

ハッシュテーブルのソートには注意してください...

Sort-Objectは、配列に変更する場合があります。

PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object


PS> $hash = $hash.GetEnumerator() | Sort-Object Name
PS> $hash.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

これと他のPowerShellループは私のブログで入手できます。


3
このように、読みやすさのために、特に私のようなPowerShell開発者を専用にしないために、もう1つ。
IBMer、2014

1
この方法の方が読みやすく、おそらくスクリプト作成に適していると思います。
leinad13 2014

2
読みやすさはさておき、VertigoRayのソリューションとAndyのソリューションには1つの違いがあります。%{}はのエイリアスでありForEach-Objectforeachここのステートメントとは異なります。 ForEach-Objectパイプラインを使用します。すでにパイプラインを使用している場合は、はるかに高速になります。foreach文はありません。単純なループです。
JamesQMurphy、2015年

4
@JamesQMurphy上から@ andy-arismendiによって提供された回答をコピーして貼り付けました。これは、この質問によく使用されるパイプラインソリューションです。私の興味のピークとなったあなたの発言は、ForEach-Object(別名%{}:)がforeach; より速いというものでした。私はそれが速くないことを示しました。私はあなたの主張が有効かどうかを実証したかったのです。私は、ほとんどの人と同じように、私のコードをできるだけ速く実行するのが好きだからです。さて、あなたの主張は、並列実行中のジョブ(複数のコンピューター/サーバーを使用している可能性があります)に変化しています...もちろん、これは単一のスレッドから連続してジョブを実行するよりも高速です。乾杯!
VertigoRay 2015

1
@JamesQMurphyまた、Powershellは、複数のプロセス間でデータストリームをパイプ処理するときにシェルが提供する従来の利点に従うと思います。各パイプラインステージが独立したプロセスであるという単純な事実から自然な並列処理が得られます(もちろん、バッファーが排出され、最後に、パイプライン全体が最も遅いプロセスと同じくらい遅くなります)。これは、プロセス自体の実装の詳細に完全に依存する、複数のスレッドを生成することを独自に決定するパイプラインプロセスを持つこととは異なります。
JohanBoulé18年

8

変数なしでもこれを行うことができます

@{
  'foo' = 222
  'bar' = 333
  'baz' = 444
  'qux' = 555
} | % getEnumerator | % {
  $_.key
  $_.value
}

これにより、「ForEach-Object:パラメータ 'Process'をバインドできません。タイプ「System.String」の「getEnumerator」値をタイプ「System.Management.Automation.ScriptBlock」に変換できません
luis.espinal

6

ハッシュのループについて:

$Q = @{"ONE"="1";"TWO"="2";"THREE"="3"}
$Q.GETENUMERATOR() | % { $_.VALUE }
1
3
2

$Q.GETENUMERATOR() | % { $_.key }
ONE
THREE
TWO

5

パイプラインを使用した列挙子メソッドでは、このバリアントをお勧めします。これは、foreachのハッシュテーブルを参照する必要がないためです(PowerShell 5でテスト済み)。

$hash = @{
    'a' = 3
    'b' = 2
    'c' = 1
}
$hash.getEnumerator() | foreach {
    Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

出力:

Key = c and Value = 1
Key = b and Value = 2
Key = a and Value = 3

現在、これは意図的に値でソートされていません。列挙子は単にオブジェクトを逆の順序で返します。

しかし、これはパイプラインであるため、列挙子から受け取ったオブジェクトを値で並べ替えることができます。

$hash.getEnumerator() | sort-object -Property value -Desc | foreach {
  Write-Host ("Key = " + $_.key + " and Value = " + $_.value);
}

出力:

Key = a and Value = 3
Key = b and Value = 2
Key = c and Value = 1

列挙子は、基礎となる実装を反復するだけなので、値を「任意の」順序で返すことができます。この場合、たまたま逆の順序で表示されます。反例の場合:$x = @{a = 1; z = 2}; $x.q = 3は、ここではq、a、zとして「順序付け」されています。
user2864740 19/07/19


4

次に、キーをハッシュテーブルのインデックスとして使用して値を取得する別の簡単な方法を示します。

$hash = @{
    'a' = 1;
    'b' = 2;
    'c' = 3
};

foreach($key in $hash.keys) {
    Write-Host ("Key = " + $key + " and Value = " + $hash[$key]);
}

0

PowerShell v3を使用している場合は、ハッシュテーブルの代わりにJSONを使用して、Convert-FromJsonでオブジェクトに変換できます。

@'
[
    {
        FileName = "Page";
        ObjectName = "vExtractPage";
    },
    {
        ObjectName = "ChecklistItemCategory";
    },
    {
        ObjectName = "ChecklistItem";
    },
]
'@ | 
    Convert-FromJson |
    ForEach-Object {
        $InputFullTableName = '{0}{1}' -f $TargetDatabase,$_.ObjectName

        # In strict mode, you can't reference a property that doesn't exist, 
        #so check if it has an explicit filename firest.
        $outputFileName = $_.ObjectName
        if( $_ | Get-Member FileName )
        {
            $outputFileName = $_.FileName
        }
        $OutputFullFileName = Join-Path $OutputDirectory $outputFileName

        bcp $InputFullTableName out $OutputFullFileName -T -c $ServerOption
    }

nbコマンドはConvertFrom-Json(およびその逆のConvertTo-Json)です。ダッシュの配置を入れ替えるだけです。
atmarx
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.