J'ai un configuration.csv
qui contient un modèle de données comme celui-ci:
| path | item | value | type | |------------|-------|--------|------| | some/path | item1 | value1 | ALL | | some/path | item2 | value3 | ALL | | other/path | item1 | value2 | SOME | | new/path | item3 | value3 | SOME |
et customization.csv
qui a un service spécifique configuration:
| path | item | value | type | |------------|-------|--------|------| | some/path | item2 | value3 | ALL | | new/path | item3 | value3 | SOME |
Mon objectif est de les fusionner et d'obtenir quelque chose comme ceci:
| path | item | value | type | |------------|-------|--------|------| | some/path | item1 | value1 | ALL | | some/path | item2 | UPDATE | ALL | | other/path | item1 | value2 | SOME |
Cela devrait ajoutez toutes les nouvelles entrées et mettez à jour toutes les entrées existantes. Aucune colonne ne peut être utilisée pour une identification unique - le chemin
et le élément
doivent être combinés, car ils sont garantis uniques.
3 Réponses :
Après de nombreuses recherches, j'ai pensé que le moyen le plus simple de manipuler les entrées sans recréer le cadre de gestion consistait à hashtable . Au cours du processus, j'ai dû tenir compte de deux cas extrêmes:
La solution finale que j'ai obtenue est la suivante:
$configuration = Import-Csv .\configuration.csv $customization = Import-Csv .\customization.csv $merged = New-Object System.Collections.ArrayList $hashTable = @{} #initializing the hashTable with the defaults foreach ($entry in $configuration) { $hashTable[$entry.path + ',' + $entry.item] = $entry.value + ',' + $entry.type } #updating the hashTable with customization that add or overwrite existing entries foreach ($entry in $customization) { $hashTable[$entry.path + ',' + $entry.item] = $entry.value + ',' + $entry.type } #the regex handles multiple commas and empty values. #It returns an empty string before and after group so we start from 1 foreach ($key in $hashTable.keys) { $psobject = [PSCustomObject]@{ path = ($key -split '(.*),(.*)')[1] item = ($key -split '(.*),(.*)')[2] value = ($hashTable[$key] -split '(.*),(.*)')[1] type = ($hashTable[$key] -split '(.*),(.*)')[2] } [void] $merged.Add($psobject) } Write-Output $merged
Une fois importé, je transforme le configuration.csv
en table de hachage avec des clés composé du chemin
et de la valeur
. Je fais ensuite de même avec customization.csv
en utilisant le même hashTable qui écrase toutes les valeurs key
existantes ou les ajoute comme nouvelles.
La troisième boucle convertit le hashTable à PSCustomObject
similaire à ce que fait Import-Csv
. J'ai divisé chacun des attributs key
et value
tout en tenant compte de plusieurs virgules et également de valeurs vides.
REMARQUE : l'expression régulière se divise à la dernière occurrence du séparateur (ici, c'est une virgule, mais vous pouvez sélectionner n'importe quoi, vraiment). Si vous souhaitez fractionner le premier, vous pouvez utiliser (. *?), (. *)
. Dans mon cas, seule la colonne value
pourrait contenir une instance du séparateur.
Si le CSV avait une colonne unique, alors une solution similaire à cette réponse aurait pu être utilisée.
Une autre alternative consiste à définir les clés comme la somme de toutes les colonnes, et cela filtrera tous les doublons dans le CSV, mais le fractionnement peut devenir délicat, en fonction des valeurs dans les colonnes.
Bonne utilisation des tables de hachage qui sont en effet rapides car elles utilisent un algorithme de recherche binaire mais je Je crains que votre solution (où vous traitez les éléments liés par des chaînes) ne puisse pas tenir pour une solution réutilisable. Comme dans votre cas, il se cassera probablement si l'un de vos fichiers contient un guillemet simple (comme Frank's File.txt
) ou une virgule que vous utilisez comme séparateur ...
Je vois votre inquiétude. Dans mon cas, j'ai testé plusieurs virgules, mais uniquement dans la variable de gauche ( chemin
ou valeur
). Les deux autres ne devraient pas avoir de virgules. Quant au '- je n'ai pas pu produire un cas où cela briserait le script. Il est traité correctement comme une chaîne.
Mon commentaire sur les guillemets est incorrect (car la chaîne de clés ne sera pas développée) mais le souci de la virgule reste, pour cela, vous pourriez envisager d'utiliser un caractère non imprimable pour séparer les champs à la place, par exemple: $ entry.path + [char] 31 + $ entry.item
(il est très peu probable que cela soit utilisé dans n'importe quel nom de propriété).
Je suggère d'utiliser Compare-Object
et comme les valeurs de customization.csv
doivent persister, utilisez les valeurs de ces fichiers comme -ReferenceObject
> Q:\Test\2019\03\01\SO_54948111.ps1 path item value type ---- ---- ----- ---- some/path item2 value3 ALL some/path item1 value1 ALL other/path item1 value2 SOME new/path item3 value3 SOME
Exemple de sortie
## Q:\Test\2019\03\01\SO_54948111.ps1 $conf = Import-Csv '.\configuration.csv' $cust = Import-Csv '.\customization.csv' $NewData = Compare-Object -ref $cust -diff $conf -Property path,item -PassThru -IncludeEqual| Select-Object -Property * -ExcludeProperty SideIndicator $NewData $NewData |Export-Csv '.\NewData.csv' -NoTypeInformation
Excellente solution. J'ai utilisé Comprae-Object
dans le passé mais évidemment pas assez pour remarquer toutes les astuces utiles.
Votre idée " utilisant le même hashTable qui écrase toutes les valeurs de clé existantes ou les ajoute comme nouvelles. " ne fonctionnera que si le chemin, élément
est unique de chaque côté car vous écraserez également les doublons ...
Considérez cette applet de commande Join-Object
.
$ configuration =
ConvertFrom-SourceTable
'
$configuration | Merge $customization -on path, item path item value type ---- ---- ----- ---- some/path item1 value1 ALL some/path item2 value3 ALL other/path item1 value2 SOME other/path item1 value3 ALL new/path item3 value3 SOME new/path item3 value4 ALL
$ customization =
ConvertFrom-SourceTable
'
| path | item | value | type | |------------|-------|--------|------| | some/path | item2 | value3 | ALL | | new/path | item3 | value3 | SOME | | new/path | item3 | value4 | ALL | '
En utilisant le Merge-Object , alias Merge
, commande proxy (voir aide):
| path | item | value | type | |------------|-------|--------|------| | some/path | item1 | value1 | ALL | | some/path | item2 | UPDATE | ALL | | other/path | item1 | value2 | SOME | | other/path | item1 | value3 | ALL | '
J'ai vu l'applet de commande lors de mes recherches, mais je voulais une solution autonome et ne reposant pas sur des dépendances externes.
Votre idée " utilisant la même table de hachage qui écrase toutes les valeurs de clé existantes ou les ajoute comme nouvelles. " ne fonctionnera que si le chemin, élément
est unique de chaque côté. Voir les résultats dans l'exemple mis à jour de ma réponse et: stackoverflow.com/a/55543321/1701026
C'était ma demande initiale dans la question - mettre à jour les valeurs de tous les chemins existants. Comme il s'agit d'un fichier de configuration, j'ai besoin que chaque entrée soit unique et ne se répète pas. Votre solution est excellente mais pour des exigences différentes. Merci d'avoir pointé les autres questions.