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

And*_*ehm 20 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
}
Run Code Online (Sandbox Code Playgroud)

输出:

PS C:\> mergehashtables $ht $ht2

cmdlet ForEach-Object at command pipeline position 1
Supply values for the following parameters:
Process[0]:
Run Code Online (Sandbox Code Playgroud)

$ ht和$ ht2是包含两个键值对的哈希表,其中一个键值对在两个哈希表中都带有键"name".

知道我做错了什么吗?

iRo*_*Ron 24

合并,散列表

您可以考虑简单地覆盖它们,而不是删除密钥:

$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
}
Run Code Online (Sandbox Code Playgroud)

对于此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
Run Code Online (Sandbox Code Playgroud)

如果提供的哈希表中存在任何重复键,则会获取最后一个哈希表的值.


(已上传2017-07-09)

Merge-Hashtables版本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
}
Run Code Online (Sandbox Code Playgroud)

句法

HashTable[] <Hashtables> | Merge-Hashtables [-Operator <ScriptBlock>]
Run Code Online (Sandbox Code Playgroud)

默认默认 情况下,重复哈希表条目中的所有值都将添加到数组中:

PS C:\> $h1, $h2, $h3 | Merge-Hashtables

Name                           Value
----                           -----
e                              1
d                              {4, 2}
b                              {8, 6}
c                              {7, 5, 3}
a                              9
Run Code Online (Sandbox Code Playgroud)

示例 要获得与版本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
Run Code Online (Sandbox Code Playgroud)

  • 更清洁,更通用的其他解决方案.特别赞赏`$ Input + $ Args`技术,我以前没见过它,它看起来很有希望用于处理管道和数组参数的超小实用程序函数 (2认同)

jon*_*n Z 16

我看到两个问题:

  1. 开放式支架应与...相同 Foreach-object
  2. 通过集合枚举时不应修改集合

以下示例说明了如何解决这两个问题:

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
}
Run Code Online (Sandbox Code Playgroud)


son*_*njz 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
Run Code Online (Sandbox Code Playgroud)


And*_*rIV 6

如果你想合并整个哈希表树

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;
}
Run Code Online (Sandbox Code Playgroud)

然后,它可以这样使用:

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
Run Code Online (Sandbox Code Playgroud)

它将产生以下输出:

{
  "a": 1,
  "d": 42,
  "c": {
    "val": "value1",
    "arr": [
      "Foo",
      "Bar"
    ]
  },
  "b": {
    "bb": 33,
    "ba": 2,
    "bc": "hello"
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我只需要这样做并发现它有效:

$HT += $HT2
Run Code Online (Sandbox Code Playgroud)

将的内容$HT2添加到 的内容中$HT

  • 除非 $HT2 具有 $HT 中已存在的密钥,否则该方法有效。然后合并会抛出异常。 (9认同)