Dave
Dave

Reputation: 187

Hashtable - Combining unknown number of hashtables

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

Answers (4)

Walter Mitty
Walter Mitty

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

Dave
Dave

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

Theo
Theo

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

vonPryz
vonPryz

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

Related Questions