Rename Hashtable Key

talkbubbleI use hashtables quite a bit. Often generating hashtables on the fly from other sources. But sometimes the hashtable keys that come from these external sources don’t align with what I intend to do with the hashtable. For example, one of the nifty things you can do with hashtables is splat them against a cmdlet or advanced function. Each hashtable key will bind to its corresponding parameter name. This makes it very easy to run a command and pass a bunch of parameters all at once. Here’s an example of splatting in action.


PS C:\> $hash = @{Computername="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> get-wmiobject @haSH

__GENUS : 2
__CLASS : AntiVirusProduct
__SUPERCLASS :
__DYNASTY : AntiVirusProduct
__RELPATH : AntiVirusProduct.instanceGuid="{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}"
__PROPERTY_COUNT : 6
__DERIVATION : {}
__SERVER : SERENITY
__NAMESPACE : ROOT\securitycenter2
__PATH : \\SERENITY\ROOT\securitycenter2:AntiVirusProduct.instanceGuid="{D68DDC3A
-831F-4fae-9E44-DA132C1ACF46}"
displayName : Windows Defender
instanceGuid : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe : %ProgramFiles%\Windows Defender\MSASCui.exe
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState : 397568
timestamp : Fri, 04 Jan 2013 17:09:42 GMT

This works because all of the hashtable keys line up with parameter names. If the names don’t match they are ignore. So sometimes I need to rename the hashtable key. This isn’t too difficult conceptually.

1. Create a new entry with the new key name and the old value
2. Remove the existing key.

But, I decided to turn this into an advanced function, probably with more bells and whistles than I really need. But maybe you will learn something new from my efforts.


Function Rename-HashTable {
#comment based help is here

[cmdletbinding(SupportsShouldProcess=$True)]

Param(
[parameter(position=0,Mandatory=$True,
HelpMessage="Enter the name of your hash table variable without the `$")]
[ValidateNotNullorEmpty()]
[string]$Name,
[parameter(position=1,Mandatory=$True,
HelpMessage="Enter the existing key name you want to rename")]
[ValidateNotNullorEmpty()]
[string]$Key,
[parameter(position=2,Mandatory=$True,
HelpMessage="Enter the NEW key name")]
[ValidateNotNullorEmpty()]
[string]$NewKey,
[switch]$Passthru,
[ValidateSet("Global","Local","Script","Private",0,1,2,3)]
[ValidateNotNullOrEmpty()]
[string]$Scope="Global"
)

#validate Key and NewKey are not the same
if ($key -eq $NewKey) {
Write-Warning "The values you specified for -Key and -NewKey appear to be the same. Names are NOT case-sensitive"
Return
}

Try {
#validate variable is a hash table
write-verbose (gv -Scope $scope | out-string)
Write-Verbose "Validating $name as a hashtable in $Scope scope."
#get the variable
$var = Get-Variable -Name $name -Scope $Scope -ErrorAction Stop

if ( $var.Value -is [hashtable]) {
#create a temporary copy
Write-Verbose "Cloning a temporary hashtable"
<#
Use the clone method to create a separate copy.
If you just assign the value to $temphash, the
two hash tables are linked in memory so changes
to $tempHash are also applied to the original
object.
#>
$tempHash = $var.Value.Clone()
#validate key exists
Write-Verbose "Validating key $key"
if ($tempHash.Contains($key)) {
#create a key with the new name using the value from the old key
Write-Verbose "Adding new key $newKey to the temporary hashtable"
$tempHash.Add($NewKey,$tempHash.$Key)
#remove the old key
Write-Verbose "Removing $key"
$tempHash.Remove($Key)
#write the new value to the variable
Write-Verbose "Writing the new hashtable"
Write-Verbose ($tempHash | out-string)
Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
Select-Object -ExpandProperty Value
}
else {
Write-Warning "Can't find a key called $Key in `$$Name"
}
}
else {
Write-Warning "The variable $name does not appear to be a hash table."
}
} #Try

Catch {
Write-Warning "Failed to find a variable with a name of $Name."
}

Write-Verbose "Rename complete"
} #end Rename-Hashtable

The function assumes you have saved the hashtable to a variable. Pass the variable name as a parameter, without the $ character. The function uses Get-Variable to retrieve the variable. Remember that variables are scope specific so I need to specify the scope to search. I’m defaulting to the global scope. Otherwise, when the function runs, it is in its own scope which doesn’t include the hashtable variable. Now, I could have simply attempted to read the variable, trusting PowerShell to read up the scope until it found it, but the best practice is to avoid referencing out of scope items.

Once the variable is found, its value is the actual hashtable which I verify as a [hashtable] object.


if ( $var.Value -is [hashtable]) {

From here I create a cloned copy of the original variable.


$tempHash = $var.Value.Clone()

By the way, you’ll notice my functions supports ShouldProcess. This means I can use -WhatIf and it will be passed to any cmdlets that also support it. At the end of my command I use Set-Variable which supports ShouldProcess so if I run my function with -WhatIf, Set-Variable will also use -Whatif. This is a long way of saying I can run the command in “test” mode without having to worry about changing the original hash table. This is also why I create a clone instead of simply setting the value of my temporary hash table to the original hash table. When you do that, the two variables are actually linked so any changes in one are made to other. Here’s an example of what I’m talking about:


PS C:\> $a = @{A=1;B=2;C=3}
PS C:\> $a

Name Value
---- -----
A 1
B 2
C 3

PS C:\> $b=$a
PS C:\> $b

Name Value
---- -----
A 1
B 2
C 3

PS C:\> $b.Add("D",4)
PS C:\> $b

Name Value
---- -----
A 1
B 2
D 4
C 3

PS C:\> $a

Name Value
---- -----
A 1
B 2
D 4
C 3

Since I want my temp copy to be “unattached”, I use the hashtable’s Clone() method. From here, it is simply a matter of creating the new key with the old value, and removing the old key with a little error handling along the way.


if ($tempHash.Contains($key)) {
#create a key with the new name using the value from the old key
Write-Verbose "Adding new key $newKey to the temporary hashtable"
$tempHash.Add($NewKey,$tempHash.$Key)
#remove the old key
Write-Verbose "Removing $key"
$tempHash.Remove($Key)
#write the new value to the variable
Write-Verbose "Writing the new hashtable"
Write-Verbose ($tempHash | out-string)
Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
Select-Object -ExpandProperty Value
}

At the end of the process I use Set-Variable to update my original hashtable variable. By default the cmdlet doesn’t write anything to the pipeline but on the chance I might need the output, I use -Passthru and send the variable value, which is the revised hashtable, back to the pipeline. With this function I can now do something like this very easily.


PS C:\> $hash = @{name="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> $hash

Name Value
---- -----
name Serenity
Class AntivirusProduct
Namespace root\securitycenter2

PS C:\> Rename-HashTable -Name hash -Key name -NewKey Computername
PS C:\> $hash

Name Value
---- -----
Class AntivirusProduct
Namespace root\securitycenter2
Computername Serenity

Now $hash has the right key names for splatting, or whatever else I have in mind. This function can come in handy with ConvertTo-HashTable. Download Rename-Hashtable and as always I hope you’ll let me know what you think.

One thought on “Rename Hashtable Key”

Comments are closed.