Hashtable keys and the case (in)sensitivity
2025-01-25I like hashtables for many of things, but you should always consider how-to use them. You should also know some things about their behavior, including their behavior regarding case sensitivity.
If you create a hashtable:
$myHashtable = @{}
and add some key-value pairs; you may not get the expected result.
If you use the Add() method of hashtable, you get an error that this key already exists.
$myHashtable.Add("Key","Value")
$myHashtable.Add("key","value")
MethodInvocationException: Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'Key1' Key being added: 'key1'"
If you use the index notation or dot notation, you get no error, but the value of the key with the name "Key" will be overwritten.
$myHashtable["Key"] = "Value"
$myHashtable["key"] = "Value"
$myHashtable.Key = "value"
$myHashtable.key = "value"
This is because the comparer used for the keys is case insensitive.
But if you create a hashtable using New-Object, the above will work and you have two keys with the same name but one with a capital letter and one only in lowercase.
$myHashtable = New-Object hashtable
$myHashtable.Add("Key","Value")
$myHashtable.Add("key","value")
In most cases this behavior is unnecessary or unwanted, but there are some use cases where it is exactly what you want.
The question is, how can you specify the comparer?
The simplest way is to use the System.StringComparer
class
$myHashtable = New-Object hashtable([System.StringComparer]::InvariantCulture)
$myHashtable = [hashtable]::new([System.StringComparer]::InvariantCulture)
Both create a hashtable with case-sensitive keys.
For hashtables with explicit defined case insensitive comparer use:
[System.StringComparer]::InvariantCultureIgnoreCase
But for what can it be necessary to have case-sensitive keys?
One good use case is combining your hashtable with regex and use it as a replacement table.
$inputString="This is a test string with some special chars we would like to replace ö ä ü Ä Ö Ü ß"
$myHashtable = New-Object hashtable([System.StringComparer]::InvariantCulture)
$MyHashtable[' ']='_'
$MyHashtable['Ň']='N'
$MyHashtable['É']='E'
$MyHashtable['ē']='e'
$MyHashtable['ö']='oe'
$MyHashtable['&']=''
$MyHashtable['ň']='n'
$MyHashtable['ī']='i'
$MyHashtable['''']=''
$MyHashtable['í']='i'
$MyHashtable['Ö']='Oe'
$MyHashtable['Ā']='A'
$MyHashtable['ā']='a'
$MyHashtable['Ž']='Z'
$MyHashtable['é']='e'
$MyHashtable[',']=''
$MyHashtable['ř']='r'
$MyHashtable['ß']='ss'
$MyHashtable['Í']='I'
$MyHashtable['Ř']='R'
$MyHashtable['Ä']='Ae'
$MyHashtable['ü']='ue'
$MyHashtable['š']='s'
$MyHashtable['Č']='C'
$MyHashtable['`']=''
$MyHashtable['ž']='z'
$MyHashtable['ä']='ae'
$MyHashtable['/']=''
$MyHashtable['Ü']='Ue'
$MyHashtable['č']='c'
$MyHashtable['-']='_'
# Build a regex pattern in the form [Characterst to replace] special chars will be escaped
$pattern = $("[" + $([regex]::Escape($( -join $MyHashtable.Keys))) + "]")
# Do an manual escaping for characters not properly escaped by [regex]::Escape
$pattern= $pattern -replace '-','\-'
# Use [regex]::Replace to replace each match with the replacement from our hashtable
$returnValue = [regex]::Replace($inputString, $pattern, { $MyHashtable[$args[0].Value] })
$returnValue
This is only a little demonstration of how to use hashtables with case-sensitive keys. In my scripts, I use this in a function where I can specify the characters and their replacements as a parameter.