
VBScript solution to Event 6 in the 2008 Winter Scripting Games.
Solutions are also available for Windows PowerShell and Perl.
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 the Dictionary object. Does that mean using the Dictionary object was the only way to complete this event? No, but like we said, it was probably the easiest way. Take a look:
Const ForReading = 1
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFile = objFS.OpenTextFile("C:\Scripts\coffee.txt", ForReading)
Set objDictionary = CreateObject("Scripting.Dictionary")
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
If InStr(strLine, "Office") = 0 Then
arrLine = Split(strLine, " ")
If objDictionary.Exists(arrLine(0)) Then
objDictionary.Item(arrLine(0)) = Int(objDictionary.Item(arrLine(0))) + arrLine(1)
Else
objDictionary.Add arrLine(0), arrLine(1)
End If
End If
Loop
objFile.Close
colKeys = objDictionary.Keys
For Each strKey in colKeys
Wscript.Echo strKey & " " & objDictionary.Item(strKey)
Next
Okay, we’ll admit, that doesn’t look easy. And we didn’t say it was easy – it was, after all, worth 10 points and we don’t want to just give points away. But we’ll walk through it and you’ll see that if you use the Dictionary object, solving this event was really pretty straightforward.
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 …
We’ll need to read from this text file, so the first thing we do is set a constant, ForReading, that we’ll use in a moment when we open the file:
Const ForReading = 1
To open a text file we use the FileSystemObject object and its OpenTextFile method:
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFile = objFS.OpenTextFile("C:\Scripts\coffee.txt", ForReading)
We create the FileSystemObject by calling the CreateObject method, then, as we mentioned, we open it with the OpenTextFile method. We pass OpenTextFile two parameters: the full path of the text file we want to open; and the ForReading constant we defined earlier, which says that we’re going to read from this file (as opposed to writing to the file).
Next we’re going to set up our Dictionary object. To do that we call CreateObject on the Scripting.Dictionary object:
Set objDictionary = CreateObject("Scripting.Dictionary")
So far so good, right? Now it’s time to start reading through the file. We do that by setting up a Do Until loop:
Do Until objFile.AtEndOfStream
With this loop, we’ll read through the file line-by-line until we reach the end; or, to use the technical term, we loop until the file’s AtEndOfStream property is True.
The first thing we do inside the loop is call the ReadLine method:
strLine = objFile.ReadLine
The first time though the loop ReadLine will read the first line of the file, the second time through it will read the second line, and so on. Once we have this line we need to figure out what to do with it. Remember that, in the file, each list of coffee orders is separated by the office to which the orders will be delivered, like this:
Office 100 Espresso 3 Latte 1 Cappuccino 1
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 InStr(strLine, "Office") = 0 Then
Here we’ve used the InStr function to determine whether the string “Office” can be found within the current line (strLine). InStr returns the position within the string where the substring was found, or returns zero if the substring was not found. In our search for Office, if InStr does not return 0 then Office was found; in that case we’ll simply skip this line and loop around to the next line. If InStr does return 0 that means that we didn’t find the word Office in this line and we can continue looking for coffee orders.
Each order is listed by coffee name, a space, and then the number of orders for that type of coffee. For example, here’s an order for three espressos:
Espresso 3
We need to separate the drink from the number (you’ll see why in a moment). We do this with the Split function:
arrLine = Split(strLine, " ")
We pass Split two parameters: the string we want to split (strLine); and the character we want to split on, in this case a blank space (“ “). Split returns an array (arrLine) containing the split-up string. Continuing with our espresso example, arrLine will look like this:
| Index | Value |
0 | Espresso |
1 | 3 |
The array item at index 0 (arrLine(0)) is equal to Espresso, while arrLine item at index position 1 (arrLine(1)) is equal to 3.
Now it’s time to start working with our Dictionary object. A Dictionary object consists of key-value pairs. Each key-value pair within a Dictionary must be unique. That’s what makes the Dictionary object 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 Dictionary, 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 Dictionary we simply add the number of orders to the value. If we find a coffee type in our list that is not in the Dictionary then we add the coffee type and number of orders as a key-value pair to the Dictionary.
Let’s walk through this one step at a time, starting with the If statement:
If objDictionary.Exists(arrLine(0)) Then
Here we’re using the Dictionary object’s Exists method to see whether the string contained in arrLine(0) (which, as we saw earlier, holds the type of coffee) is already in the Dictionary. If it is, we need to add the number of orders to the value of that key, like this:
objDictionary.Item(arrLine(0)) = Int(objDictionary.Item(arrLine(0))) + arrLine(1)
Yes, there are a lot of parentheses in this line, aren’t there? Let’s break this down. Remember, arrLine(0) contains the type of coffee, arrLine(1) contains the number of orders. We reference a value in a Dictionary by calling the Item method, passing Item the key name for the value we want to return. That’s what this does:
objDictionary.Item(arrLine(0))
To the right of the equal sign we used this method call as the parameter to the Int function:
Int(objDictionary.Item(arrLine(0)))
When we read the value (the number of orders) from the Dictionary object, it comes back as a string. We need to add this value to the number of orders we read from the file, which means we need to explicitly turn this value into and integer; that way, VBScript knows to add the two values rather than simply concatenate them. In other words, if our Dictionary object has a key of Espresso and a value of 5, and we add 3 more espressos, we want the result to come back as 8, not 53. And speaking of adding, that’s exactly what we’re doing here:
Int(objDictionary.Item(arrLine(0))) + arrLine(1)
We’re taking the integer value of the value associated with the Dictionary key corresponding to the coffee type in arrLine(0) and adding it to the number of orders in arrLine(1). Then we’re assigning the result back to our Dictionary value for that key:
objDictionary.Item(arrLine(0)) = Int(objDictionary.Item(arrLine(0))) + arrLine(1)
If the coffee type read from the current line doesn’t already exist in the Dictionary, we fall into the Else clause and add the key and value (coffee type and number of orders) to the Dictionary using the Dictionary’s Add method:
objDictionary.Add arrLine(0), arrLine(1)
If that was all a little confusing, take a look at the Sesame Script article where we explain the Dictionary object in more detail.
After we’ve read through the entire text file, our Dictionary object will contain one entry for each type of coffee, with the total number of each type ordered by all the offices combined. After we close the text file (using, what else, the Close method), all that’s left is to echo back the results:
colKeys = objDictionary.Keys
For Each strKey in colKeys
Wscript.Echo strKey & " " & objDictionary.Item(strKey)
Next
To echo back the results we need to loop through the collection of keys in our Dictionary object. We retrieve that collection like this:
colKeys = objDictionary.Keys
Now we can use a For Each loop to loop through the colKeys collection and echo back the key and the value associated with that key:
Wscript.Echo strKey & " " & objDictionary.Item(strKey)
This was kind of a long explanation, but once you start using the Dictionary object you’ll find it really can be very useful and isn’t as difficult as it might first seem.
Oh, and here’s our final tally:
Espresso 13 Latte 22 Cappuccino 18