在Powershell中合并哈希表:如何?

时间:2012-01-10 08:28:43

标签: powershell hashtable

我正在尝试合并两个哈希表,如果第二个中存在相同的键,则覆盖第一个键值对。

为了做到这一点,我写了这个函数,如果第二个哈希表中存在相同的密钥,它首先删除第一个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”。

知道我做错了吗?

12 个答案:

答案 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)

Merge-Hashtables版本2

一般来说,我更喜欢更多的全局函数,可以根据特定需求自定义参数,如原始问题所示:"如果第二个&#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)

我看到两个问题:

  1. 开放式大括号应与Foreach-object
  2. 位于同一行
  3. 在枚举集合时不应修改集合
  4. 以下示例说明了如何解决这两个问题:

    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语法
  • 不是幂等的。我添加了HashTable输入的克隆,否则你的输入被破坏了,而不是意图
  • 添加了一个正确的使用示例
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[$_]}
}

我是从https://stackoverflow.com/a/33839784/880076

借来的

答案 10 :(得分:0)

这里是进行深度合并并支持有序哈希表的人。

https://github.com/majkinetor/posh/blob/master/MM_HashTables/Merge-Hashtables.ps1

答案 11 :(得分:-1)

我只需要这样做,发现这有效。

$ HT + = $ HT2

$ HT2的内容被添加到$ HT

的内容中

/安德鲁