Reputation: 187
I'm trying to programmatically combine an unknown number of hashtables into a larger hashtable. Each individual table will have the same keys. I tried just appending it, but it throws an error about duplicate keys.
ForEach ($Thing in $Things){
$ht1 = New-Object psobject @{
Path = $Thing.path
Name = $Thing.name
}
$ht2 += $ht1
}
That throws the error
Item has already been added. Key in dictionary: 'Path' Key being added: 'Path'
The end result would be that later I can say
ForEach ($Item in $ht2){
write-host $Item.path
write-host $Item.name
}
Upvotes: 0
Views: 252
Reputation: 18940
Converting my comment to an answer.
What you probably want to create is an array of hashtables. Each item in the array can have its own value for each key. This structure can be used in the way you indicate in your query at the end of your post.
Try this:
$things = gci $home
$ht2 = @() # create empty array
ForEach ($Thing in $Things){
$ht1 = New-Object psobject @{
Path = $Thing.PSpath
Name = $Thing.name
}
$ht2 += $ht1
}
$ht2
Note that I changed .path to .PSpath in order to make the example work. Note that $ht2 gets initialized to an empty array before looping.
Upvotes: 1
Reputation: 187
Turns out what I needed for my results was an array of hashtables, not a hashtable of hashtables, as pointed out by @WalterMitty. My final code was:
#varibale name ht2 kept for clarity in how it relates to original question
$ht2 = @()
ForEach ($Thing in $Things){
$ht1 = New-Object psobject @{
Path = $Thing.path
Name = $Thing.name
}
$ht2 += $ht1
}
Upvotes: 0
Reputation: 61093
If this really is about merging Hashtables as the title says, then basically you have two options to add the entries from the second hashtable into the first.
1. Use the static method Add()
(first item 'wins')
This has already been explained in vonPryz's answer, but in short:
Adding an entry in a hashtable with a key that already exists in the hash, the Add()
method will throw an exception, because all keys in a hash must be unique.
To overcome that, you need to check if an entry with that key exists and if so, do not add the entry from the second hash.
foreach ($key in $secondHash.Keys) {
if (!$firstHash.Contains($key)) {
# only add the entry if the key (name) did not already exist
$firstHash.Add($key, $secondHash[$key])
}
}
This way, all entries already in the first hashtable will NOT get overwritten and duplicate entries from the second hash are discarded.
2. Overwriting/adding regardless of the existance (last item 'wins')
You can also opt to 'merge' the entries without the need for checking like this:
foreach ($key in $secondHash.Keys) {
# always add the entry even if the key (name) already exist
$firstHash[$key] = $secondHash[$key]
}
This way, if an entry already existed in the first hash, its value will be overwritten with the value from the second hash. If the entry did not already exist, it is simply added to the first hashtable.
But, what if you want to merge without skipping or overwriting existing values?
In that case, you need to come up with some method of creating a unique key for the entry to add.
Something like this:
foreach ($key in $secondHash.Keys) {
if ($firstHash.Contains($key)) {
# ouch, the key already exists..
# now, we only want to add this if the value differs (no point otherwise)
if ($firstHash[$key] -ne $secondHash[$key]) {
# add the entry, but create a new unique key to store it under first
# this example just adds a number between brackets to the key
$num = 1
do {
$newKey = '{0}({1})' -f $key, $num++
} until (!$firstHash.Contains($newKey))
# we now have a unique new key, so add it
$firstHash[$newKey] = $secondHash[$key]
}
}
else {
# no worries, the key is unique
$firstHash[$key] = $secondHash[$key]
}
}
Upvotes: 0
Reputation: 24071
Adding an element to a hash table indeed fails if there already is an element with same key. To overcome that, first see if the element exists. Like so,
if(-not $ht2.ContainsKey($thing.name)){
$ht2.Add($thing.name, $thing.value)
}
It is also possible to use item property []
for the check, since it returns $null
for non-existing element. Like so,
if($ht2[$thing.name] -eq $null){
$ht2.Add($thing.name, $thing.value)
}
Upvotes: 0