同じキーが2番目に存在する場合、最初のハッシュテーブルのキーと値のペアを上書きして、2つのハッシュテーブルをマージしようとしています。
これを行うために、同じキーが2番目のハッシュテーブルに存在する場合、最初に最初のハスタブルのすべてのキーと値のペアを削除するこの関数を作成しました。
これをPowerShellに1行ずつ入力すると、機能します。しかし、関数全体を実行すると、PowerShellは、欠落しているパラメーターをforeach-objectに提供するように要求します。
function mergehashtables($htold, $htnew)
{
$htold.getenumerator() | foreach-object
{
$key = $_.key
if ($htnew.containskey($key))
{
$htold.remove($key)
}
}
$htnew = $htold + $htnew
return $htnew
}
出力:
PS C:\> mergehashtables $ht $ht2
cmdlet ForEach-Object at command pipeline position 1
Supply values for the following parameters:
Process[0]:
$ htと$ ht2は、それぞれ2つのキーと値のペアを含むハッシュテーブルであり、そのうちの1つは両方のハッシュテーブルにキー「name」があります。
私は何が間違っているのですか?
2つの問題があります。
Foreach-object
と同じ行にある必要があります以下の例は、両方の問題を修正する方法を示しています。
function mergehashtables($htold, $htnew)
{
$keys = $htold.getenumerator() | foreach-object {$_.key}
$keys | foreach-object {
$key = $_
if ($htnew.containskey($key))
{
$htold.remove($key)
}
}
$htnew = $htold + $htnew
return $htnew
}
キーを削除する代わりに、単にキーを上書きすることを検討してください。
_$h1 = @{a = 9; b = 8; c = 7}
$h2 = @{b = 6; c = 5; d = 4}
$h3 = @{c = 3; d = 2; e = 1}
Function Merge-Hashtables {
$Output = @{}
ForEach ($Hashtable in ($Input + $Args)) {
If ($Hashtable -is [Hashtable]) {
ForEach ($Key in $Hashtable.Keys) {$Output.$Key = $Hashtable.$Key}
}
}
$Output
}
_
このコマンドレットでは、いくつかの構文を使用でき、2つの入力テーブルに制限されません。パイプラインの使用:_$h1, $h2, $h3 | Merge-Hashtables
_
引数の使用:_Merge-Hashtables $h1 $h2 $h3
_
または組み合わせ:_$h1 | Merge-Hashtables $h2 $h3
_
上記の例はすべて同じハッシュテーブルを返します。
_Name Value
---- -----
e 1
d 2
b 6
c 3
a 9
_
提供されたハッシュテーブルに重複するキーがある場合は、最後のハッシュテーブルの値が取得されます。
(2017-07-09追加)
一般に、元の質問のように、特定のニーズに合わせてパラメーターを使用してカスタマイズできる、よりグローバルな関数を好みます:「同じキーが2番目に存在する場合、最初のキーと値のペアを上書きする」 。なぜ最初のものではなく最後のものを却下させるのですか?なぜ何も削除しないのですか?たぶん、他の誰かが値をマージまたは結合したり、最大の値または単に平均を取得したい...
以下のバージョンでは、ハッシュテーブルを引数として指定することはサポートされていません(ハッシュテーブルを関数にパイプすることしかできません)が、値配列を操作して重複エントリの値配列を処理する方法を決定できるパラメーターがあります現在のオブジェクト(_$_
_)に表示されるハッシュキーに割り当てられます。
関数
_Function Merge-Hashtables([ScriptBlock]$Operator) {
$Output = @{}
ForEach ($Hashtable in $Input) {
If ($Hashtable -is [Hashtable]) {
ForEach ($Key in $Hashtable.Keys) {$Output.$Key = If ($Output.ContainsKey($Key)) {@($Output.$Key) + $Hashtable.$Key} Else {$Hashtable.$Key}}
}
}
If ($Operator) {ForEach ($Key in @($Output.Keys)) {$_ = @($Output.$Key); $Output.$Key = Invoke-Command $Operator}}
$Output
}
_
構文
_HashTable[] <Hashtables> | Merge-Hashtables [-Operator <ScriptBlock>]
_
デフォルトデフォルトでは、複製されたハッシュテーブルエントリのすべての値が配列に追加されます。
_PS C:\> $h1, $h2, $h3 | Merge-Hashtables
Name Value
---- -----
e 1
d {4, 2}
b {8, 6}
c {7, 5, 3}
a 9
_
例バージョン1と同じ結果を得るには(最後の値を使用)、コマンド_$h1, $h2, $h3 | Merge-Hashtables {$_[-1]}
_を使用します。代わりに最初の値を使用する場合、コマンドは_$h1, $h2, $h3 | Merge-Hashtables {$_[0]}
_または最大値:$h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Maximum).Maximum}
です。
その他の例:
_PS C:\> $h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Average).Average} # Take the average values"
Name Value
---- -----
e 1
d 3
b 7
c 5
a 9
PS C:\> $h1, $h2, $h3 | Merge-Hashtables {$_ -Join ""} # Join the values together
Name Value
---- -----
e 1
d 42
b 86
c 753
a 9
PS C:\> $h1, $h2, $h3 | Merge-Hashtables {$_ | Sort-Object} # Sort the values list
Name Value
---- -----
e 1
d {2, 4}
b {6, 8}
c {3, 5, 7}
a 9
_
新しい答えではありません。これは機能的には@ Josh-Petittと同じですが、改善されています。
この答えでは:
Merge-HashTable
これをモジュールにドロップする場合は、正しいPowerShell構文を使用しますfunction Merge-HashTable {
param(
[hashtable] $default, # Your original set
[hashtable] $uppend # The set you want to update/append to the original set
)
# Clone for idempotence
$default1 = $default.Clone();
# We need to remove any key-value pairs in $default1 that we will
# be replacing with key-value pairs from $uppend
foreach ($key in $uppend.Keys) {
if ($default1.ContainsKey($key)) {
$default1.Remove($key);
}
}
# Union both sets
return $default1 + $uppend;
}
# Real-life example of dealing with IIS AppPool parameters
$defaults = @{
enable32BitAppOnWin64 = $false;
runtime = "v4.0";
pipeline = 1;
idleTimeout = "1.00:00:00";
} ;
$options1 = @{ pipeline = 0; };
$options2 = @{ enable32BitAppOnWin64 = $true; pipeline = 0; };
$results1 = Merge-HashTable -default $defaults -uppend $options1;
# Name Value
# ---- -----
# enable32BitAppOnWin64 False
# runtime v4.0
# idleTimeout 1.00:00:00
# pipeline 0
$results2 = Merge-HashTable -default $defaults -uppend $options2;
# Name Value
# ---- -----
# idleTimeout 1.00:00:00
# runtime v4.0
# enable32BitAppOnWin64 True
# pipeline 0
中括弧はForEach-Object
と同じ行にある必要があります。そうでない場合は、行継続文字(バッククォート)を使用する必要があります。
これは、{...}内のコードが、実際には-Process
コマンドレットのForEach-Object
パラメーターの値であるためです。
-Process <ScriptBlock[]>
Specifies the script block that is applied to each incoming object.
これにより、目前の現在の問題を乗り越えることができます。
ハッシュテーブルのアイテムによってオーバーライド(またはオーバーロード)されている可能性があるため、ジェネリック関数でハッシュテーブルの基本プロパティを無差別に参照するべきではないことを指摘したいと思います。
たとえば、ハッシュテーブル_$hash=@{'keys'='lots of them'}
_には基本ハッシュテーブルプロパティがあり、Keys
はアイテムkeys
によってオーバーライドされるため、foreach ($key in $hash.Keys)
を実行すると代わりにハッシュが列挙されます。基本プロパティkeys
の代わりに、item Keys
の値。
代わりに、メソッドGetEnumeratorまたはkeys
プロパティのPSBase
プロパティは、オーバーライドできませんが、基本プロパティがオーバーライドされているかどうかわからない関数で使用する必要があります。
したがって、ジョンZの答えは最高です。
jon Zの答え を拡張または単純化したかっただけです。行が多すぎて、使用する機会を逃したようです Where-Object 。これが私の簡略版です:
Function merge_hashtables($htold, $htnew) {
$htold.Keys | ? { $htnew.ContainsKey($_) } | % {
$htold.Remove($_)
}
$htold += $htnew
return $htold
}
function Join-HashTableTree {
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[hashtable]
$SourceHashtable,
[Parameter(Mandatory = $true, Position = 0)]
[hashtable]
$JoinedHashtable
)
$output = $SourceHashtable.Clone()
foreach ($key in $JoinedHashtable.Keys) {
$oldValue = $output[$key]
$newValue = $JoinedHashtable[$key]
$output[$key] =
if ($oldValue -is [hashtable] -and $newValue -is [hashtable]) { $oldValue | ~+ $newValue }
elseif ($oldValue -is [array] -and $newValue -is [array]) { $oldValue + $newValue }
else { $newValue }
}
$output;
}
次に、次のように使用できます。
Set-Alias -Name '~+' -Value Join-HashTableTree -Option AllScope
@{
a = 1;
b = @{
ba = 2;
bb = 3
};
c = @{
val = 'value1';
arr = @(
'Foo'
)
}
} |
~+ @{
b = @{
bb = 33;
bc = 'hello'
};
c = @{
arr = @(
'Bar'
)
};
d = @(
42
)
} |
ConvertTo-Json
次の出力が生成されます。
{
"a": 1,
"d": 42,
"c": {
"val": "value1",
"arr": [
"Foo",
"Bar"
]
},
"b": {
"bb": 33,
"ba": 2,
"bc": "hello"
}
}
子ハッシュテーブル内の既存のキーの値を変更せずに、親ハッシュテーブル($htOld
)から子ハッシュテーブル($htNew
)にキー値を「継承」するには、
function MergeHashtable($htOld, $htNew)
{
$htOld.Keys | %{
if (!$htNew.ContainsKey($_)) {
$htNew[$_] = $htOld[$_];
}
}
return $htNew;
}
これにより、$htNew
オブジェクトが変更されることに注意してください。
これはパイプラインを使用しない関数バージョンです(パイプラインが悪いわけではなく、それを行う別の方法です)。また、マージされたハッシュテーブルを返し、元のハッシュテーブルを変更しません。
function MergeHashtable($a, $b)
{
foreach ($k in $b.keys)
{
if ($a.containskey($k))
{
$a.remove($k)
}
}
return $a + $b
}
最もコンパクトなコードはこれだと思います:
function Merge-Hashtables($htold, $htnew)
{
$htnew.keys | where {$_ -notin $htold.keys} | foreach {$htold[$_] = $htnew[$_]}
}
PowerShellのハッシュテーブルの結合と共通部分から借用しました