PowerShellで簡単な配列比較

さまざまなオブジェクトのPowershell配列を比較するには、PowerShellの知識を利用して簡単に行うことができます。さまざまなシナリオで使用できる方法を見てみましょう。

配列を比較するためには、まず両方の配列に含まれる要素のタイプを特定する必要があります。

  • 両方の配列には同じタイプのオブジェクトが含まれていますか?
  • 両方の配列には同じ数の要素がありますか?
  • それぞれの配列に異なるタイプのオブジェクトが含まれていますか?

正確な配列の比較を行うには、これらの質問の答えを知る必要があります。それぞれのシナリオについて説明しましょう。

文字列の配列の比較

PowerShellで配列を比較する最も簡単な方法の1つは、2つの配列が文字列のみを含んでいる場合です。この場合、配列内の文字列を比較するためにいくつかの異なる方法があります。

-Containsまたは-In演算子の使用

-Contains演算子は、オブジェクトがコレクションに含まれているかどうかを確認するためのPowerShell演算子です。-Contains演算子は元々コレクションを理解しませんが、コードを組み立てて使用することができます。

以下のように、コレクション(配列)には4つの文字列が含まれているとします。

$array = @('blue','red','purple','pink')

-Contains演算子は以下のように、左側のコレクションがその文字列を含んでいるかどうかをチェックします。

$array -contains 'pink'

左側のコレクションがその文字列を含んでいる場合、PowerShellはTrueを返します。そうでない場合はFalseを返します。

PowerShell -contains Operator

-containsオペレータを使用して配列を比較することができます。それぞれの文字列を読み取り、他の配列がその文字列を含んでいるかどうかを確認します。

例えば、最初の配列内の文字列が2番目の配列内に存在するかどうかを比較したい場合、以下のようになります。

$array = @('blue','red','purple','pink')
$array2 = @('brown','red','black','yellow')

$array | ForEach-Object {
    if ($array2 -contains $_) {
        Write-Host "`$array2 contains the `$array1 string [$_]"
    }
}

逆に、-inオペレータを使用することもできます。このオペレータは-containsオペレータと同じですが、構文が逆になります。-containsオペレータを使用する場合は、配列は左側に定義されます。-inオペレータを使用する場合は、配列は右側に定義されます。以下のようになります。

$array | ForEach-Object {
    if ($_ -in $array2) {
        Write-Host "`$array2 contains the `$array1 string [$_]"
    }
}

Where-Objectを使用する

もしくは、Where-Objectコマンドレットを使用して、一方の配列内のすべての文字列をもう一方の配列に返すこともできます。

$array | Where-Object -FilterScript { $_ -in $array2 }

Compare-Objectコマンドレットを使用する

また、PowerShellを使用して、Compare-Objectコマンドレットを使用して配列を比較することもできます。このコマンドレットは、参照オブジェクトと差分オブジェクトを取り、どちらの配列に要素が存在しているかを示すサイドインジケータを返します。

Compare-Object -ReferenceObject $array -DifferenceObject $array2
Using Compare-Object

以下の例では、Compare-Objectコマンドレットを使用して両方の配列を一度に比較しています。もしSideIndicatorプロパティが=>であれば、InputObjectプロパティはDifferenceObjectの値に存在し、ReferenceObjectの値には存在しないことを示し、<=SideIndicatorはその逆です。

デフォルトでは、Compare-Objectは差分を返します。また、IncludeEqualパラメータを使用することで、両方の配列に含まれるすべての文字列を返すこともできます。

Comparing arrays with Compare-Object

複雑なオブジェクトの比較

簡単ですね。これにオブジェクトを組み合わせてみましょう。例えば、このHRデータベースにはフィールドがあり、これをActive Directoryの説明フィールドに入れたいとします。これを行う前に、共通の識別子が必要です。私の環境では、HRデータベースとカスタムActive Directory属性の両方に従業員番号があります。それでは、これをマッチングしましょう。

まず、前のアプローチを試してみた場合に何が起こるか見てみましょう。使用しているCSVファイルは次のとおりです。

CSV output

次に、2つのデータセットを取得する方法です。

$ad_users = Get-AdUser -Filter {enabled -eq $true} -Properties employeeNumber | select employeenumber,samaccountname,description
$users_from_database = Import-Csv 'database_users.csv' | select employee number

これらの2つの配列を前と同じシナリオで実行した場合、何も起こりません。なぜですか?

その理由は、通常$object1 -eq $object2のようには言えないからです。オブジェクトは単純な文字列、ブール値、整数よりも複雑です。これが当てはまらない場合もありますが、私は通常、オブジェクトのプロパティを比較する習慣を持っています。したがって、この場合は次のようにする必要があります:

$ad_user[0].employeeNumber -eq $users_from_database[0].employeeNumber

解決策は何ですか?現在、2つの解決策があります。数千のオブジェクトを扱う場合は速くありませんが、動作します。他にどのような提案があるかを知りたいと思っています。

$ad_employee_numbers = $ad_users | % {$_.employeenumber}

## ADユーザーの従業員番号のみの文字列配列を作成する
$users_from_database | Where-Object -FilterScript { $ad_employee_numbers -contains $_.employeeNumber }

Compare-Objectも使用することができますが、これは遅くなります。

$ad_employee_numbers = $ad_users | ForEach-Object {$_.employeenumber}

## ADユーザーの従業員番号のみの文字列配列を作成する
$database_user_employee_numbers = $users_from_database | ForEach-Object {$_.employeenumber}

## データベースユーザーの従業員番号のみの文字列配列を作成する
(Compare-Object $ad_employee_numbers $database_user_employee_numbers -IncludeEqual | Where-Object -FilterScript {$_.SideIndicator -eq '=='}).InputObject

結論

PowerShellを使用して配列を比較するためのさまざまな方法があります。配列は複雑であり、配列内のオブジェクトを完全に理解することは、比較に非常に役立ちます。

Source:
https://adamtheautomator.com/powershell-compare-arrays/