Automatically Create Hash Tables and Reverse Values

Doctor Scripto

Summary: Learn how to automatically create hash tables and reverse keys and values. Microsoft Scripting Guy, Ed Wilson, is here. The other day, I was playing around with Windows PowerShell, and thought I would create a couple of hash tables automatically. One hash table would contain all of the lowercase ASCII letters in the order of ASCII value, then lowercase letter. The other would reverse the order, and contain the lowercase letters first, and then the ASCII value. I could then use the hash tables as look-up tables for making automatic conversions. As I said, I was playing around with these things, and I realized that the process might actually provide some value for someone at some time. I took me several hours to make things work out the way I wanted them to; so hopefully, this will make it easier on you when the time comes. At the outset, I will say that this is more or less a hypothetical couple of blog posts—more of a solution in search of a problem kind of thing. To demonstrate the process, I am going way back to when I was in grade school and I wanted to pass notes to my friends. I used a basic cypher to encode my notes, and to hide their contents from the prying eyes of my teachers. It was not anything super cryptic to be sure; but evidently, it was good enough to protect the contents from my teachers. For I will admit, although I got in trouble for passing notes, I never got in trouble for what the notes said—probably a good thing. Letter/number substitution is one of the oldest kinds of cryptography. In the most basic system, it works like this:

A = 1

B = 2

C = 3 So if I have a note that has: 1 3 2, then I know that I am saying A C B. That simple. We already have a letter substitution table built-in to computers: ASCII encoding. It has uppercase and lowercase letters in addition to other symbols. I am only going to use the lowercase letters for my hash tables. I can convert a number to a letter by using the [char] class. So, I convert 97 into the lowercase a as follows:

PS C:> [char]97

a And I convert the lowercase z from 122 as shown here:

PS C:> [char]122

z I use the range operator to create numbers that are equivalent to the ASCII range of lowercase a to lowercase z. This is shown here:

97..122

First things first

The first thing I want to do is to initialize a couple of variables and create a couple of empty hash tables. I use the variable $ASCIIFirst to hold the hash table that contains the ASCII number first as the key, and the letter equivalent as the value. The $ltrfirst variable is the reverse. It contains the lowercase letter as the key, and the ASCII numeric value as the associated value. Here are the first couple of lines that accomplish this:

$ASCIIFirst=$ltrFirst=$null

$ASCIIFirst = @{}

$ltrFirst = @{}

Create the first hash table

Now I am going to create the $ASCIIFirst hash table. To do this, I use the range operator to create the numbers 97 through 122. I pipe these numbers to the Foreach-Object cmdlet, and I use the empty hash table contained in the $ASCIIFirst variable to first add the number as the key and then the letter as the associated value. To get the letter value, I use the [char] to convert the number and show the associated ASCII character with it. But this leaves me with the [char] datatype, which is not a string. To get the string, I use the ToString method. This is not important here, but it will be vital when I want to do a lookup from the hash table by using a letter character. A [char] will not work with a string character.

97..122 |

  Foreach-Object {

    $ASCIIFirst.Add($_,([char]$_).ToString())}

Now flip the hash table

Now, I want to flip my hash table, or reverse it, or turn it inside out…however you want to describe it. What I am really doing is making all of the keys in my new hash table into values. I take all of the associated values, and in the new hash table, I make them keys. To do this, I use the Foreach command (not the Foreach-Object cmdlet), and I use the $k to walk through all of the keys from the $ASCIIFirst hash table. Inside the script block, I use the Add method, and I use the key from the $ASCIIFirst hash table to look up the associated value with that key. I now add the value as the new key into the hash table stored in the $ltrFirst hash table. I then store the key in the value that is associated with my new key. It sounds confusing, and it really is. But study the following code to get the idea:

foreach($k in $ASCIIFirst.Keys)

    {$ltrFirst.add($ASCIIFirst[$k],$k)} Here is the complete code as it currently stands:

$ASCIIFirst=$ltrFirst=$null

$ASCIIFirst = @{}

$ltrFirst = @{}

97..122 |

  Foreach-Object {

    $ASCIIFirst.Add($_,([char]$_).ToString())}  

foreach($k in $ASCIIFirst.Keys)

    {$ltrFirst.add($ASCIIFirst[$k],$k)} That is all there is to using Windows PowerShell to create a hash table and to reverse the keys and values. It is not a whole lot of code, to be honest, but it is really a powerful technique. Tomorrow I will show you how to use this to convert words into numbers, and then translate the numbers back into words. If only I had something like this back when I was in the third grade. I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace. Ed Wilson, Microsoft Scripting Guy

1 comment

Discussion is closed. Login to edit/delete existing comments.

  • Tracy MacShane 0

    To make this a little more useful for the typical range of printable ASCII characters (rather than just the lowercase letters), you can make the hashtables case-sensitive. Otherwise, if you define ASCII 32..126 in the first hashtable, it complains about duplicate keys (for the upper and lower-case letters) when you reverse it.
    I haven’t exhaustively tested the following for the keys that are non-letter characters, but a sample including spaces, asterixes and various other punctuation symbols worked fine.
    $ASCIIFirst=$ltrFirst=$null$ASCIIFirst = New-Object system.collections.hashtable32..126 | Foreach-Object {    $ASCIIFirst.Add($_,([char]$_).ToString())}$ltrFirst = New-Object system.collections.hashtable foreach($k in $ASCIIFirst.Keys) {    $ltrFirst.add($ASCIIFirst[$k],$k)}
    $txt = ‘ ‘$txt.ToCharArray() | % {$ltrFirst[“$_”]}32
    $txt = ‘*’$txt.ToCharArray() | % {$ltrFirst[“$_”]}42
    $txt = ‘A’$txt.ToCharArray() | % {$ltrFirst[“$_”]}65
    $txt = ‘a’$txt.ToCharArray() | % {$ltrFirst[“$_”]}97

Feedback usabilla icon