Working with Hash Tables

 

 

In last week’s Windows PowerShell Tip we introduced you to the .NET Framework class System.Collections.ArrayList, positioning this class as an alternative to the array class built into Windows PowerShell. (Why do you even needan alternative to the array class built into Windows PowerShell? Well, for one thing, the ArrayList class makes it easy to remove items from the array, something that’s nearly impossible to do otherwise.) In that same column we promised that, this week, we’d discuss another alternative to the generic Windows PowerShell array: the hash table. Well, never let it be said that the Scripting Guys don’t always keep their promises.

 

Note. OK, so technically we don’talways keep our promises. We just never want that to be said.

 

Although the term hash tablemight be new to many of you, there’s a good chance that you’re familiar with the concept; after all, a hash table is simply a collection of name-value pairs, very much like the FileSystemObject’s Dictionaryobject. For example, suppose you have a collection of US states and their capitals. Each state has one – and only one – capital (e.g., the state capital of Washingtonis Olympia ), and a given city can be the capital of only one state. If you wanted to keep track of – and make use of – that information, you couldconstruct a two-dimensional array. Or, you could take a much easier route and create a hash table.

 

Note. Yes, we know: we didn’t really go over the concept of key-value pairs, did we? But that’s OK. The Dictionary object explanation found in the Microsoft Windows 2000 Scripting Guide applies equally well to the Windows PowerShell hash table.

 

But enough with the talk; let’s see some code. Suppose you really wouldlike a hash table containing a list of US states and their capitals; how would you go about creating such a thing? Well, here’s one way:

 

$states = @{" Washington" = " Olympia"; " Oregon" = " Salem"; California= " Sacramento "}

 

Granted this is a bit cryptic-looking at least at first glance; fortunately, however, it’s nowhere near as complicated as it might appear. All we’re doing here is assigning three sets of key-value pairs to a hash table named $states. As you can see, the syntax for creating a hash table involves using an at sign (@) followed by a pair of curly braces.

 

Note. The @ symbol is know as the at sign only in English; in other languages , it’s known as way cooler things, like maggot, monkey’s tail, and pickled herring.

 

So what goes inside those curly braces? Well, that’s where we specify the key-value pairs, using the syntax key = value; for example, " Washington" = " Olympia ". Note to that we separate the individual key-value pairs using semicolons; hence the syntax " Washington" = " Olympia"; " Oregon" = " Salem ".

 

And what will the variable $states be equal to after we issue our hash table command? Why, this, of course:

 

Name                           Value
----                           -----
California                      Sacramento
Washington                      Olympia
Oregon                          Salem

 

That’s pretty good, except for one thing: our hash table is far from complete. (After all, we only list 3 USstates, and there are actually … uh … more than 3 states in the US .) So how do you add a new key-value pair to an existing hash table? Well, we don’t know about you, but here’s how the Scripting Guys do it:

 

$states.Add(" Alaska", " Fairbanks ")

 

Note. Yes, yes, we know: Fairbanksis notthe capital of Alaska . We’ll get to that in just a moment.

 

Needless to say, there’s nothing too fancy going on here: we simply call the Addmethod, passing this method two parameters: the new key ( " Alaska") and the new value ( " Fairbanks "). Now take a look at the value of $states:

 

Name                           Value
----                           -----
California                      Sacramento
Alaska                          Fairbanks
Washington                      Olympia
Oregon                          Salem

 

Incidentally, that’s a good observation: you never know exactly where a new entry will appear in the hash table. In this case, for example, our new entry somehow became item 2 in a 4-item table. But that’s OK; before we go we’ll show you how to test for the existence of a key or a value regardless of their position in the hash table. And, just for the heck of it, we’ll show you how to sort the table as well.

 

But first things first. As the tidal wave of emails and phone calls has made very clear, the state capital of Alaskaisn’t Fairbanksafter all; it’s Juneau . Unfortunately, however, we’ve already added this incorrect key-value pair to our hash table. Now what are we going to do?

 

Well, one thing we coulddo is simply remove the offending item. Want to get rid of the key-value pair for Alaska ? Okey-doke; what else would you use the Removemethod for?

 

$states.Remove(" Alaska ")

 

Just to be on the safe side, let’s take a look at the value of $states:

 

Name                           Value
----                           -----
California                      Sacramento
Washington                      Olympia
Oregon                           Salem

 

That’s better, isn’t it?

 

Alternatively, we could have used the Set_Itemmethod to change the value assigned to Alaska :

 

$states.Set_Item(" Alaska", " Juneau ")

 

Run that command and then take a look at the value of $states; it should be equal to the following:

 

Name                           Value
----                           -----
California                      Sacramento
Alaska                          Juneau
Washington                      Olympia
Oregon                          Salem

 

Incidentally, the Set-Item method has a corollary method named Get_Item; this method enables us to retrieve the value associated with a specific item in the hash table. Not sure which city is the capital of Oregon ? Then run this command and find out for yourself:

 

$states.Get_Item(" Oregon ")

 

Now, suppose that the state of Oregon didn’t appear anywhere in your hash table; what happens if you issue the preceding command and the item can’t be found? Well, actually, nothing: you don’t get a return value of any kind, but you don’t get an error message, either. And that’s actually a bit of problem: it’s often a bit tricky to try to figure out what really happened when nothing seems to have happened.

 

Fortunately, you can use the ContainsKeyand the ContainsValuemethods to search for items in a hash table. Need to know if an item named Oregon really doesexist in your hash table? Then just call the ContainsKey method, like so:

 

$states.ContainsKey(" Oregon ")

 

This method returns True if it finds an item named Oregonand False if it doesn’tfind an item named Oregon . Likewise, you can search for a specified value by using the ContainsValue method:

 

$states.ContainsValue(" Salem ")

 

Note. If you just need to know how many items are in a hash table you can simply report back the value of the Countproperty: $states.Count.

 

Last, but surely not least, let’s talk about sorting a hash table. This can be a little tricky; this command will notwork:

 

$states | Sort-Object

 

Why not? Well, in the preceding command the hash table is sent as a single object; thus there’s nothing for the Sort-Objectcmdlet to sort. If we want to sort a hash table by Name we need to use the GetEnumeratormethod, which effectively sends each entry in the hash table across the pipeline as a separate object:

 

$states.GetEnumerator() | Sort-Object Name

 

Which, in turn, gives us output like this:

 

Name                           Value
----                           -----
Alaska                           Juneau
California                      Sacramento
Oregon                          Salem
Washington                      Olympia

 

Here’s a similar command, except that this one sorts the hash table by Value and (just because we wanted to show off a little) in descending order to boot:

 

$states.GetEnumerator() | Sort-Object Value -descending

 

And here’s what the output of thatcommand looks like:

 

Name                           Value
----                           -----
Oregon                         Salem
California                     Sacramento
Washington                     Olympia
Alaska                         Juneau

 

Pretty cool, huh?