Fusionner des hashtables dans Powershell: comment?
j'essaye de fusionner deux hashtables, en écrasant les paires clé-valeur dans la première si la même clé existe dans la seconde.
pour ce faire, j'ai écrit cette fonction qui supprime d'abord toutes les paires de clés dans le premier hastable si la même clé existe dans le second hashtable.
quand je tape ceci dans Powershell ligne par ligne ça marche. Mais quand J'exécute la fonction entière, Powershell me demande de fournir (ce qu'il considère) les paramètres manquants pour foreach-object.
function mergehashtables($htold, $htnew)
{
$htold.getenumerator() | foreach-object
{
$key = $_.key
if ($htnew.containskey($key))
{
$htold.remove($key)
}
}
$htnew = $htold + $htnew
return $htnew
}
Sortie:
PS C:> mergehashtables $ht $ht2
cmdlet ForEach-Object at command pipeline position 1
Supply values for the following parameters:
Process[0]:
$ht et $ht2 sont des hashtables contenant deux paires de clés chacune, l'une avec la clé "name" dans les deux hashtables.
une idée de ce que je fais de mal?
10 réponses
je vois deux problèmes:
- le corset ouvert doit être sur la même ligne que
Foreach-object
- vous ne devriez pas modifier une collection tout en énumérant à travers une collection
L'exemple ci-dessous illustre comment résoudre ces deux problèmes:
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
}
Fusionner Les, Tables De Hachage
au Lieu de supprimer les clés, vous pourriez envisager de simplement remplacer:
$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
}
pour ce cmdlet vous pouvez utiliser plusieurs syntaxes et vous n'êtes pas limité à deux tables d'entrée:
Utilisation du pipeline:$h1, $h2, $h3 | Merge-Hashtables
Utilisation des arguments:Merge-Hashtables $h1 $h2 $h3
Ou une combinaison: $h1 | Merge-Hashtables $h2 $h3
Tous les exemples ci-dessus renvoient la même table de hachage:
Name Value
---- -----
e 1
d 2
b 6
c 3
a 9
S'il y a des clés dupliquées dans le les tables de hachage, la valeur de la dernière table de hachage est pris.
(Ajouté 2017-07-09)
Fusionner les, tables de hachage version 2
en général, je préfère des fonctions plus globales qui peuvent être personnalisées avec des paramètres à des besoins spécifiques comme dans la question originale: "l'écrasement de paires clé-valeur dans la première si la même clé existe dans le deuxième". Pourquoi laisser le dernier annuler et pas la première? Pourquoi les enlever quoi que ce soit? Peut-être quelqu'un d'autre veut fusionner ou joindre les valeurs ou obtenir la plus grande valeur ou juste la moyenne...
La version ci-dessous ne supporte plus la fourniture de tables de hachage comme arguments (vous ne pouvez utiliser que les tables de hachage pipe pour la fonction) mais a un paramètre qui vous permet de décider comment traiter le tableau de valeurs dans les entrées en double en utilisant le tableau de valeurs assigné à la clé de hachage présentée dans l'objet courant ($_
).
Function
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
}
Syntaxe
HashTable[] <Hashtables> | Merge-Hashtables [-Operator <ScriptBlock>]
par Défaut Par défaut, toutes les valeurs des entrées de la table de hachage dupliquées seront ajoutées à un tableau:
PS C:\> $h1, $h2, $h3 | Merge-Hashtables
Name Value
---- -----
e 1
d {4, 2}
b {8, 6}
c {7, 5, 3}
a 9
Exemples
Pour obtenir le même résultat que la version 1 (en utilisant dernières valeurs) utilisez la commande:$h1, $h2, $h3 | Merge-Hashtables {$_[-1]}
. Si vous souhaitez utiliser le valeurs à la place, la commande est: $h1, $h2, $h3 | Merge-Hashtables {$_[0]}
ou valeurs les plus élevées:$h1, $h2, $h3 | Merge-Hashtables {($_ | Measure-Object -Maximum).Maximum}
.
plus d'exemples:
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
ce n'est pas une nouvelle réponse, c'est fonctionnellement la même chose que @Josh-Petitt avec des améliorations.
Dans cette réponse:
Merge-HashTable
utilise la bonne syntaxe powershell si vous voulez déposer ceci dans un module- N'était pas idempotent. J'ai ajouté le clonage de l'input HashTable, sinon votre input a été saboté, pas une intention
- ajout d'un bon exemple d'utilisation
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
L'open corset doit être sur la même ligne que ForEach-Object
ou vous devez utiliser le caractère de continuation de ligne (backtick).
C'est le cas parce que le code dans { ... } est vraiment la valeur de l' -Process
paramètre ForEach-Object
applet de commande.
-Process <ScriptBlock[]>
Specifies the script block that is applied to each incoming object.
cela vous permettra de dépasser le problème actuel à portée de main.
je voulais simplement développer ou simplifier la réponse de jon Z. Il semble juste y avoir trop de lignes et d'occasions manquées d'utiliser où-objet. Voici ma version simplifiée:
Function merge_hashtables($htold, $htnew) {
$htold.Keys | ? { $htnew.ContainsKey($_) } | % {
$htold.Remove($_)
}
$htold += $htnew
return $htold
}
je voulais faire remarquer qu'il ne faut pas référencer les propriétés de base du hashtable de façon indistincte dans les fonctions génériques, car elles peuvent avoir été supplantées (ou surchargées) par des éléments du hashtable.
par exemple, le hashtable $hash=@{'keys'='lots of them'}
la base de la table de hachage de la propriété, Keys
remplacé par l'élément keys
, et donc de faire un foreach ($key in $hash.Keys)
énumérera à la place l'item hashé keys
' s valeur, au lieu de la propriété de base Keys
.
à la Place de la méthode GetEnumerator ou le keys
propriété de l' PSBase
la propriété, qui ne peut pas être annulée, doit être utilisée dans des fonctions qui n'ont aucune idée si les propriétés de base ont été annulées.
ainsi, la réponse de Jon Z est la meilleure.
Voici une fonction version qui n'utilise pas le pipeline (pas que le pipeline est mauvais, juste une autre façon de le faire). Il renvoie aussi un hashtable fusionné et laisse l'original inchangé.
function MergeHashtable($a, $b)
{
foreach ($k in $b.keys)
{
if ($a.containskey($k))
{
$a.remove($k)
}
}
return $a + $b
}
je pense que le plus compact de code serait ceci:
function Merge-Hashtables($htold, $htnew)
{
$htnew.keys | where {$_ -notin $htold.keys} | foreach {$htold[$_] = $htnew[$_]}
}
je l'ai emprunté à partir de https://stackoverflow.com/a/33839784/880076
encore une réponse!
Pour "hériter" clé-valeurs du parent hashtable ($htOld
) pour enfant les tables de hashage($htNew
), sans modifier les valeurs des clés déjà existantes dans les hashtables enfant,
function MergeHashtable($htOld, $htNew)
{
$htOld.Keys | %{
if (!$htNew.ContainsKey($_)) {
$htNew[$_] = $htOld[$_];
}
}
return $htNew;
}
veuillez noter que ceci modifiera l'objet $htNew.
j'ai juste besoin de faire cela et trouvé cela fonctionne.
$HT += $HT2
le contenu de $HT2 est ajouté au contenu de $HT
/Andrew