
Windows PowerShell solution to Event 6 in the 2008 Winter Scripting Games.
To successfully complete this event you needed to be able to read from a text file. Also, if you did it the easy way, you needed to use a hash table. Does that mean using a hash table was the only way to complete this event? No, but like we said, it was probably the easiest way. Take a look:
$coffee = @{}
$a = Get-Content C:\Scripts\coffee.txt
foreach ($b in $a)
{
$c = $b.split(" ")
if ($c[0] -ne "Office")
{
if ($coffee.ContainsKey($c[0]))
{
$coffee.set_item($c[0], [int]$c[1] + [int]$coffee.get_item($c[0]))
}
else
{
$coffee.add($c[0], $c[1])
}
}
}
$coffee;
Working with hashes can seem a bit complicated, but it’s really not that bad -- honest. We’ll walk through this solution and explain how it all works. We’re going to start by declaring our hash:
$coffee = @{}
Yes, this is how you declare an empty hash. We’ll get back to this in a moment. But first, remember that, for this event we need to read in the contents of a text file, a text file that looks something like this:
Office 100 Espresso 3 Latte 1 Cappuccino 1 Office 200 Cappuccino 2 …
The way to read from a text file in Windows PowerShell is by using the Get-Content cmdlet, passing Get-Content the name of the file we want to read and assigning the contents to the variable $a:
$a = Get-Content C:\Scripts\coffee.txt
At this point the entire contents of the file coffee.txt are stored in $a as an array, with each line of the file being an item in the array. And how do we read through arrays? That’s right, with a foreach loop:
foreach ($b in $a)
The first time though the loop $b will contain the first line of the file, the second time through it will contain the second line, and so on. The first thing we’re going to do with each line is use the split function to split it into an array:
$c = $b.split(" ")
We’re splitting the line on a blank space, meaning every time we run into a blank space on the line we create a new array element. For example, suppose we’re reading the first line, Office 100. Here’s what’s in our array ($c) after we split the line:
| Index | Value |
0 | Office |
1 | 100 |
That brings up an interesting point. All we want to do is count the orders; we don’t care about the office number. That means that, as we read through the file, we want to skip all the lines that contain office numbers. We do that with this If statements:
if ($c[0] -ne "Office")
Here we’re checking the first item in the array, the item at index number 0. As we just saw from our table, if the line has an office number on it item 0 will be the string “Office”. So we check to see if $c[0] is equal to “Office”. If it is we ignore that line, go back to the beginning of the loop, and start again on the next line. The next line is again split and creates this array:
| Index | Value |
0 | Espresso |
1 | 3 |
The array item at index 0 ($c[0]) is equal to Espresso, while $c item at index position 1 ($C[1]) is equal to 3, the number of orders.
Now it’s time to start working with our hash table. A hash table consists of key-value pairs. Each key within a hash table must be unique. That’s what makes the hash table ideal for solving this event. We want to collect all the different types of coffee, and add the number of orders to each type. The way we’re going to do this is by adding each coffee type as a key to the hash table, with the number of orders being the corresponding value for each of those keys. If we find a coffee type in our list that’s already in the hash table we simply add the number of orders to the value. If we find a coffee type in our list that is not in the hash table, we add the coffee type and number of orders as a key-value pair to the hash.
Let’s walk through this one step at a time, starting with the If statement:
if ($coffee.ContainsKey($c[0]))
Here we’re using the ContainsKey method to see whether the string contained in $c[0], which, as we saw earlier, holds the type of coffee, is already in the hash table. If it is, we need to add the number of orders to the value of that key, like this:
$coffee.set_item($c[0], [int]$c[1] + [int]$coffee.get_item($c[0]))
Yes, there are a lot of parentheses and brackets in this line, aren’t there? Let’s break this down. Remember, $c[0] contains the type of coffee and $c[1] contains the number of orders. We set a value in a hash table by calling the set_item method, passing set_item two parameters: the key name for the value we want to set and the value associated with that key. The key name is easy; we already know that’s the first item in our array:
$c[0]
The value in this case is a little more complicated:
[int]$c[1] + [int]$coffee.get_item($c[0])
Let’s look at the second part of this equation first:
[int]$coffee.get_item($c[0])
We know that the key (the type of coffee) already exists in the hash table. So what we need to do is look up that entry in the table and retrieve the associated value. We do that by calling the get_item function on the hash table, passing it the name of the key, which is exactly what we’ve done here. We’ve also included [int] in front of this function call. The reason for that? Well, we want to make sure Windows PowerShell treats this value as a number rather than a string. We can ensure it does that by “casting” the value as an integer.
Once we have the value associated with the key in $c[0] we add that to the value we just read from the text file (which, as we said, is stored as the second item in our array ($c[1])). As you can see, we cast that value as an integer as well, and for the same reason: so PowerShell will treat the value as an integer rather than a string so we can add it.
In other words, what we’ve done with this equation is take the integer value of the value associated with the key corresponding to the coffee type in $c[0] and add it to the number of orders in $c[1].
If the coffee type read from the current line doesn’t already exist in the hash table we fall into the else clause and add the key and value (coffee type and number of orders) to the hash table using the add method:
$coffee.add($c[0], $c[1])
If that was all a little confusing, take a look at the Windows PowerShell Tip of the Week where we explain hash tables in more detail.
After we’ve read through the entire text file, our hash table will contain one entry for each type of coffee, with the total number of each type ordered by all the offices combined. All that’s left is to display the results:
$coffee;
This was kind of a long explanation, but once you start using hash tables you’ll find they really can be very useful, and they’re not quite as difficult as it might first seem.
Oh, and here’s our final tally:
Name Value ---- ----- Espresso 13 Latte 22 Cappuccino 18