powershell hashtable

Fusionando tablas hash en Powershell: ¿cómo?



powershell hashtable foreach (10)

Estoy intentando fusionar dos tablas hash, sobrescribiendo los pares clave-valor en la primera si existe la misma clave en la segunda.

Para hacer esto, escribí esta función que primero elimina todos los pares clave-valor en el primer hasta si existe la misma clave en la segunda tabla hash.

Cuando escribo esto en Powershell línea por línea funciona. Pero cuando ejecuto toda la función, Powershell me pide que proporcione (lo que considera) los parámetros faltantes para cada objeto.

function mergehashtables($htold, $htnew) { $htold.getenumerator() | foreach-object { $key = $_.key if ($htnew.containskey($key)) { $htold.remove($key) } } $htnew = $htold + $htnew return $htnew }

Salida:

PS C:/> mergehashtables $ht $ht2 cmdlet ForEach-Object at command pipeline position 1 Supply values for the following parameters: Process[0]:

$ ht y $ ht2 son tablas hash que contienen dos pares clave-valor cada uno, uno de ellos con la clave "nombre" en ambas tablas hash.

¿Alguna idea de lo que estoy haciendo mal?


Fusionar-Hashtables

En lugar de eliminar las claves, podría considerar simplemente sobrescribirlas:

$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 }

Para este cmdlet puede usar varias sintaxis y no está limitado a dos tablas de entrada: Usando la tubería: $h1, $h2, $h3 | Merge-Hashtables $h1, $h2, $h3 | Merge-Hashtables
Usando argumentos: Merge-Hashtables $h1 $h2 $h3
O una combinación: $h1 | Merge-Hashtables $h2 $h3 $h1 | Merge-Hashtables $h2 $h3
Todos los ejemplos anteriores devuelven la misma tabla hash:

Name Value ---- ----- e 1 d 2 b 6 c 3 a 9

Si hay claves duplicadas en las tablas hash proporcionadas, se toma el valor de la última tabla hash.

(Añadido 2017-07-09)

Merge-Hashtables versión 2

En general, prefiero funciones más globales que se pueden personalizar con parámetros para necesidades específicas como en la pregunta original: "sobrescribir los pares clave-valor en la primera si existe la misma clave en la segunda" . ¿Por qué dejar que la última anule y no la primera? ¿Por qué quitar algo en absoluto? Tal vez alguien más quiera fusionar o unir los valores o obtener el valor más alto o simplemente el promedio ...
La versión a continuación ya no admite el suministro de tablas hash como argumentos (solo puede canalizar tablas hash a la función) pero tiene un parámetro que le permite decidir cómo tratar la matriz de valores en entradas duplicadas al operar la matriz de valores asignada a la clave hash Presentado en el objeto actual ( $_ ).

Función

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 }

Sintaxis

HashTable[] <Hashtables> | Merge-Hashtables [-Operator <ScriptBlock>]

De forma predeterminada De forma predeterminada, todos los valores de las entradas de tabla hash duplicadas se agregarán a una matriz:

PS C:/> $h1, $h2, $h3 | Merge-Hashtables Name Value ---- ----- e 1 d {4, 2} b {8, 6} c {7, 5, 3} a 9

Ejemplos Para obtener el mismo resultado que la versión 1 (usando los últimos valores ) use el comando: $h1, $h2, $h3 | Merge-Hashtables {$_[-1]} $h1, $h2, $h3 | Merge-Hashtables {$_[-1]} . Si desea utilizar los primeros valores , el comando es: $h1, $h2, $h3 | Merge-Hashtables {$_[0]} $h1, $h2, $h3 | Merge-Hashtables {$_[0]} o los valores más grandes : $h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Maximum).Maximum} $h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Maximum).Maximum} .

Más ejemplos:

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


¡Otra respuesta más!

Para ''heredar'' valores-clave de la tabla hash principal ( $htOld ) a las $htOld hash ( $htNew ), sin modificar los valores de las claves ya existentes en las tablas hash secundarias,

function MergeHashtable($htOld, $htNew) { $htOld.Keys | %{ if (!$htNew.ContainsKey($_)) { $htNew[$_] = $htOld[$_]; } } return $htNew; }

Tenga en cuenta que esto modificará $ htNew objeto.


Aquí hay una versión de la función que no usa la canalización (no es que la tubería sea mala, solo es otra forma de hacerlo). También devuelve una tabla hash combinada y deja el original sin cambios.

function MergeHashtable($a, $b) { foreach ($k in $b.keys) { if ($a.containskey($k)) { $a.remove($k) } } return $a + $b }


Creo que el código más compacto sería este:

function Merge-Hashtables($htold, $htnew) { $htnew.keys | where {$_ -notin $htold.keys} | foreach {$htold[$_] = $htnew[$_]} }

Lo tomé prestado de https://.com/a/33839784/880076


La abrazadera abierta debe estar en la misma línea que ForEach-Object o usted debe usar el carácter de continuación de línea (comilla invertida).

Este es el caso porque el código dentro de {...} es realmente el valor para el parámetro -Process del cmdlet ForEach-Object .

-Process <ScriptBlock[]> Specifies the script block that is applied to each incoming object.

Esto te llevará más allá del problema actual.


No es una respuesta nueva, esto es funcionalmente lo mismo que @ Josh-Petitt con mejoras.

En esta respuesta:

  • Merge-HashTable usa la sintaxis correcta de powershell si desea colocar esto en un módulo
  • No era idempotente Agregué la clonación de la entrada de HashTable, de lo contrario su entrada fue obstruida, no una intención
  • añadido un ejemplo adecuado de uso

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


Quería señalar que no se deben hacer referencia indiscriminadamente a las propiedades base de la tabla hash en funciones genéricas, ya que pueden haber sido anuladas (o sobrecargadas) por elementos de la tabla hash.

Por ejemplo, la tabla $hash=@{''keys''=''lots of them''} tendrá la propiedad base de la tabla hash, las Keys reemplazadas por las keys del elemento y, por lo tanto, hacer un foreach ($key in $hash.Keys) enumere el valor de las keys elementos con hash, en lugar de las Keys propiedad base.

En su lugar, el método GetEnumerator o la propiedad de keys de la propiedad PSBase , que no se puede anular, deben usarse en funciones que pueden no tener idea si las propiedades base se han anulado.

Por lo tanto, la respuesta de Jon Z es la mejor.


Solo necesitaba hacer esto y encontré esto funciona.

$ HT + = $ HT2

Los contenidos de $ HT2 se agregan a los contenidos de $ HT2.

/Andrés


Solo quería expandir o simplificar la respuesta de jon Z. Parece que hay demasiadas líneas y oportunidades perdidas para usar Where-Object. Aquí está mi versión simplificada:

Function merge_hashtables($htold, $htnew) { $htold.Keys | ? { $htnew.ContainsKey($_) } | % { $htold.Remove($_) } $htold += $htnew return $htold }


Veo dos problemas:

  1. La abrazadera abierta debe estar en la misma línea que Foreach-object
  2. No debes modificar una colección mientras se enumera a través de una colección.

El siguiente ejemplo ilustra cómo solucionar ambos problemas:

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 }