我正在尝试合并两个哈希表,如果第二个中存在相同的键,则覆盖第一个键值对。
为了做到这一点,我写了这个函数,如果第二个哈希表中存在相同的密钥,它首先删除第一个hastable中的所有键值对。
当我逐行输入Powershell时,它可以工作。但是当我运行整个函数时,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是哈希表,每个哈希表包含两个键值对,其中一个键哈希表中包含键“name”。
知道我做错了吗?
答案 0 :(得分:21)
您可以考虑简单地覆盖它们,而不是删除键:
$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
}
对于此cmdlet,您可以使用多种语法,并且不限于两个输入表:
使用管道:$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)
一般来说,我更喜欢更多的全局函数,可以根据特定需求自定义参数,如原始问题所示:"如果第二个&#34中存在相同的键,则覆盖第一个键值对# 34; 。为什么让最后一个否决而不是第一个呢?为什么删除任何东西?也许其他人想要合并或加入价值或获得最大价值或只是平均值...
下面的版本不再支持提供散列表作为参数(您只能将散列表传递给函数),但有一个参数可以让您通过操作分配给散列键的值数组来决定如何处理重复条目中的值数组在当前对象($_
)中显示。
<强>功能强>
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
答案 1 :(得分:15)
我看到两个问题:
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
}
答案 2 :(得分:7)
这不是一个新的答案,这在功能上与@ 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
答案 3 :(得分:4)
开括号必须与ForEach-Object
在同一行,否则你必须使用续行符(反引号)。
这是因为{...}中的代码实际上是-Process
cmdlet的ForEach-Object
参数的值。
-Process <ScriptBlock[]>
Specifies the script block that is applied to each incoming object.
这将使您了解当前的问题。
答案 4 :(得分:1)
我只想扩展或简化jon Z的答案。似乎有太多的线条,错过了使用Where-Object的机会。这是我的简化版本:
Function merge_hashtables($htold, $htnew) {
$htold.Keys | ? { $htnew.ContainsKey($_) } | % {
$htold.Remove($_)
}
$htold += $htnew
return $htold
}
答案 5 :(得分:1)
又一个答案!
从父哈希表($htOld
)'继承'键值到子哈希表($htNew
),而不修改子哈希表中现有键的值,
function MergeHashtable($htOld, $htNew)
{
$htOld.Keys | %{
if (!$htNew.ContainsKey($_)) {
$htNew[$_] = $htOld[$_];
}
}
return $htNew;
}
请注意,这将修改$ htNew对象。
答案 6 :(得分:1)
我想指出的是,不应在泛型函数中不加区别地引用哈希表的基本属性,因为它们可能已被哈希表的项目覆盖(或重载)。
例如,哈希表$hash=@{'keys'='lots of them'}
将具有基本的哈希表属性Keys
被项目keys
覆盖,因此执行foreach ($key in $hash.Keys)
将枚举哈希项keys
的值,而不是基础属性Keys
。
相反,不能覆盖基本属性的函数应该使用无法覆盖的方法GetEnumerator或keys
属性的PSBase
属性。
因此,乔恩·Z的答案是最好的。
答案 7 :(得分:1)
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"
}
}
答案 8 :(得分:0)
这是一个不使用管道的函数版本(不是管道坏,只是另一种方法)。它还返回一个合并的哈希表,并保持原始状态不变。
function MergeHashtable($a, $b)
{
foreach ($k in $b.keys)
{
if ($a.containskey($k))
{
$a.remove($k)
}
}
return $a + $b
}
答案 9 :(得分:0)
我认为最紧凑的代码是:
function Merge-Hashtables($htold, $htnew)
{
$htnew.keys | where {$_ -notin $htold.keys} | foreach {$htold[$_] = $htnew[$_]}
}
借来的
答案 10 :(得分:0)
这里是进行深度合并并支持有序哈希表的人。
https://github.com/majkinetor/posh/blob/master/MM_HashTables/Merge-Hashtables.ps1
答案 11 :(得分:-1)
我只需要这样做,发现这有效。
$ HT + = $ HT2
$ HT2的内容被添加到$ HT
的内容中/安德鲁