2008 Winter Scripting Games

Solution to Beginner Perl Event 6: Coffee Break

Event 6 Solution


Perl solution to Event 6 in the 2008 Winter Scripting Games.

Solutions are also available for VBScript and Windows PowerShell.

*

Event 6 – Coffee Break

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 = ();

open (TEXT, 'C:\\scripts\\coffee.txt');
@orders = <TEXT>;

for $line (@orders)
{
    @c = split(/ /, $line);
    if ($c[0] ne "Office")
    {

        if (exists $coffee{$c[0]})
        {
            $coffee{$c[0]} = ($c[1] + $coffee{$c[0]});
        }
        else
        {
            $coffee{$c[0]} = $c[1];
        }
    }
}

while (($key, $value) = each %coffee)
{
    print "$key $value\n";
}

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 Perl is by calling the open function and passing it two parameters: a variable into which the open method will place an object reference to the file, and the path to the file we want to open:

open (TEXT, 'C:\\scripts\\coffee.txt');

Once we have the file open we can read the entire contents of the file into an array, like this:

@orders = <TEXT>;

At this point the entire contents of the file coffee.txt are stored in the @orders 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:

for $line (@orders)

Note: Why does our foreach loop start with for rather than foreach? That’s just shorthand in Perl. You can spell out foreach if you want.

The first time though the loop $line 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 = split(/ /, $line);

We’re splitting the line on a blank space (indicated by the blank space between the slash marks / /), meaning that 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:

IndexValue

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")

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:

IndexValue

0

Espresso

1

3

The array item at index 0 ($c[0]) is equal to Espresso, while the 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 (exists $coffee{$c[0]})

Here we’re using the exists 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{$c[0]} = ($c[1] + $coffee{$c[0]});

Yes, that is a little confusing, isn’t it? Let’s break this down. Remember, $c[0] contains the type of coffee, and $c[1] contains the number of orders. In Perl we retrieve the value associated with a key in a hash table by referencing the hash table with that key, like this:

$coffee{$c[0]}

Let’s say the current line we’ve read from the table is an order for 3 Espressos. The array item $c[0] contains the string “Espresso”. So what we’re doing here is assigning a value to the hash table entry with a key of “Espresso”. And just what value are we assigning? Well, we’re assigning the current value associated with the Espresso key (which we already know is located here: $coffee{$c[0]}) added to the number of orders we just read in (3), which is in our array $c[1]. See how that works?

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. That’s as simple as assigning a value to the hash table with the key we want to add:

$coffee{$c[0]} = $c[1];

That all might have been a little confusing, but play around with it for a while and you’ll see it works pretty well.

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. There are at least two ways to do this (probably more). We’ll show you both ways that we came up with. First we display the results using a while loop:

while (($key, $value) = each %coffee)
{
    print "$key $value\n";
}

We use the while loop to loop through the entire hash table, displaying the results one at a time with the print statement. But let’s take a closer look at the while statement:

while (($key, $value) = each %coffee)

Let’s examine the right side of the equation first: each %coffee. This simply tells our loop that we’re going to be looping through each record in the %coffee hash table. The loop will end when we reach the end of the hash table.

Looking at the left side of the equation, the each statement is assigning the key and value from each record to the variables on the left: $key and $value. Now when we print the results we simply print the $key variable to display the key (the type of drink) and the $value variable to display the value (the total number of orders for that drink):

print "$key $value\n";

If you’re interested in another way of looping through a hash table, here’s one more option, an option that uses a foreach loop:

for $key (keys %coffee)
{
    print "$key $coffee{$key}\n";
}

This foreach loop will loop through all the keys in the %coffee hash table. We already saw earlier that we can access the value associated with a given key by including the key in curly braces following the name of the hash table, like this: $coffee{$key}. And that’s exactly what we do here to print the key and the value:

print "$key $coffee{$key}\n";

There’s almost always multiple ways of doing the same thing in Perl; it’s up to you to find the one that works best for you.

Oh, and here’s our final tally:

Espresso 13
Latte 22
Cappuccino 18

Top of pageTop of page