In Powershell you can use .Add to insert a new key/value pair into an existing hash. If the hash already contains the key, this leads to an error. The desired (by me :) behavior would be, for known keys, just udate the existing value to the provided one. I can do this with a lot of ink. Putting the .Add command in a try phrase and in the catch the changing of the value - which works fine, but the cost of ink!
Seriously, as I have this kind logic all over the place when parsing multiple configs (was this already set and needs updating or is it a new setting?), it makes for messy code:
# $msHashtable is potentially empty at this point or may not contain the key
try {
$myHashtable.Add($thisKey, $thisValue)
}
catch {
$myHashtable.$thisKey = $thisValue
}
Another issue with hashes that I have is this:
I can provide code if needed, but I hope the issues are clear enough. As there is so much other code than the relevant pieces in my real world example, I'll restrain from posting it now...
Best,
YeOldHinnerk
You already got a helpful answer for the first part of your question.
This is my try at the second part - how to assign members of nested hash tables. There isn't an easy built-in syntax to set nested values while creating any not-yet-existing parent hash tables, so I've created a reusable function Set-TreeValue
for that purpose.
function Set-TreeValue( $HashTable, [String] $Path, $Value, [String] $PathSeparator = '\.' ) {
# To detect errors like trying to set child of value-type leafs.
Set-StrictMode -Version 3.0
do {
# Split into root key and path remainder (", 2" -> split into max. 2 parts)
$key, $Path = $Path -split $PathSeparator, 2
if( $Path ) {
# We have multiple path components, so we may have to create nested hash table.
if( -not $HashTable.Contains( $key ) ) {
$HashTable[ $key ] = [ordered] @{}
}
# Enter sub tree.
$HashTable = $HashTable[ $key ]
}
else {
# We have arrived at the leaf -> set its value
$HashTable[ $key ] = $Value
}
}
while( $Path )
}
Demo:
$ht = [ordered] @{}
Set-TreeValue $ht foo.bar.baz 42 # Create new value and any non-existing parents
Set-TreeValue $ht foo.bar.baz 8 # Update existing value
Set-TreeValue $ht foo.bar.bam 23 # Add another leaf
Set-TreeValue $ht fop 4 # Set a leaf at root level
#Set-TreeValue $ht fop.zop 16 # Outputs an error, because .fop is a leaf
Set-TreeValue $ht 'foo bar' 15 # Use a path that contains spaces
$ht | ConvertTo-Json -Depth 99 # Output the content of the hash table
Output:
{
"foo": {
"bar": {
"baz": 8,
"bam": 23
}
},
"fop": 4,
"foo bar": 15
}
NOTE: I've opted to create nested hash tables as OrderedDictionary
as these are much more useful than regular ones (e. g. to ensure an order in a JSON output). Remove [ordered]
if you want unordered hash tables (which propably have slight performance advantage).