Arrays are the first construct that VBScript instructors introduce when they discuss how to group data. With arrays, you can store columns of data in one place, then access the data later through one variable. However, years of real-world use have revealed that arrays aren't always the most desirable solution to gather and maintain related data. Fortunately, a new type of array has emerged: the dictionary. Here's a look at what dictionaries are and how you manipulate them with the methods and properties that Table 1 shows.
Learn more: Dictionaries Trump Arrays
Comparing Dictionaries and Arrays
A dictionary is a general-purpose data structure that looks like a linked list but acts like a "super array." Like VBScript arrays, dictionaries store data and make that data available through one variable. (If you're unfamiliar with VBScript arrays, see my July 1999 column.) However, dictionaries differ from arrays in many ways, including
- A dictionary has additional methods to add new items and check for existing items.
- You don't need to call ReDim to extend the dictionary's size.
- When you delete a particular item from a dictionary, all the subsequent items automatically shift up. For example, if you delete the second item in a three-item dictionary, the original third item automatically shifts up into the second-item slot.
- You use keys to identify dictionary items. Keys can be any data subtype, except an array or dictionary.
- A dictionary can't be multidimensional. (Although you can't store arrays or dictionaries in a dictionary item, you can store dictionaries in array items.)
The most important reason for using a dictionary instead of an array is that a dictionary is more flexible and is richer in terms of built-in functionality. Dictionaries work better than arrays when you need to access random elements frequently. Dictionaries also work better when you want to locate items by their content rather than their position.
Creating a Dictionary
A dictionary is a COM object (called the Dictionary object) that is available through the Microsoft Scripting Runtime library. The Dictionary object isn't part of the VBScript runtime engine but rather a separate COM object that Microsoft bundles with any installation of VBScript and JScript. As a result, you can use the Dictionary object in any COM-compliant script, including VBScript, JScript, and Perl scripts.
To create a Dictionary object in VBScript code, you use the CreateObject function with the programmatic identifier (ProgID) of Scripting.Dictionary:
- Dim dict
- Set dict = CreateObject _
After you've created an instance of the Dictionary object, you're ready to add items to it.
To add items to a dictionary, you use the Add method. You can't insert an item in a specific position in a dictionary; any item you add automatically appears at the bottom of the dictionary.
The Add method has the syntax
- dObject.Add key, item
where dObject is the Dictionary object you want to add items to. You must specify two arguments. The first argument, key, specifies the unique key you want to use to identify the item. The second argument, item, is the value you want the item to store. For example, the code
- dict.Add "Italy", "Rome"
- dict.Add "Germany", "Berlin"
adds two items to the dictionary named dict. The first item's value is Rome. You use the key Italy to access this item. The second item's value is Berlin. You use the key Germany to access this item. In other words, you're adding two key-value pairs—Italy-Rome and Germany-Berlin—to this dictionary.
In a key-value pair, only the key must be unique. A dictionary can have key-value pairs that have the same value. For example, a dictionary might include the items Today's grocery list-Milk and Tomorrow's grocery list-Milk.
If you attempt to add a key-value pair but the key you specify already exists in that dictionary, you receive a runtime error. To avoid this error, you can use the Exists method, which checks whether a key is unique. This method has the syntax
where key is the key you want to check. This method returns True if the key exists and False if it doesn't. You can use this method in code similar to that in Listing 1. This code checks the key of a new key-value pair before adding the item to the dictionary. If the key already exists, you receive the message Please specify a new key rather than a runtime error. If the key doesn't exist, the code adds the item's key and value.
As I mentioned earlier, a key can be any data subtype, except an array or dictionary. (For information about the various VBScript subtypes, see my June 1999 column.) For example, you can create a dictionary that uses unique integers rather than strings for keys:
- dict.Add 1, "Rome"
- dict.Add 2, "Berlin"
Although using integers or other subtypes is permissible, most scriptwriters use strings because strings are the most readable.
To retrieve existing items in a dictionary, you use the Item property. This read/write property has the syntax
where key is the key of the item whose value you want to read. For example, if you want to retrieve and display the value that the Italy item holds, you specify
- MsgBox dict.Item("Italy")
However, you can also use the abbreviated syntax
- MsgBox dict("Italy")
to display the value Rome because the Item property is the Dictionary object's default property.
If the key is a string, the case in which you specify the key is important. By default, the Dictionary object makes a binary comparison when it tries to find a match between the specified key and the stored keys. Thus, the comparison is case sensitive. So, for example, the code
- MsgBox dict("ITALY")
won't display a value because you initially entered the key in title case (i.e., Italy) not upper case (i.e., ITALY). If you try to retrieve a key that doesn't exist, you don't get an error but rather an empty string.
If you want to make a case-insensitive comparison, you can use the Dictionary object's CompareMode property. The code
- dict.CompareMode = _
forces the object to apply a textual comparison when searching for a match. Therefore, the comparison is case insensitive. You can set the CompareMode property only when the dictionary is empty. Attempting to set this property for a nonempty dictionary results in an error.
Enumerating Items' Keys and Values
VBScript arrays use progressive 0- or 1-based indexes—numbers over which you have no control—to identify items. As a result, you can easily locate an item based on its position in the array, but you can't easily locate an item based on its content. Dictionaries use keys—strings or numbers over which you have complete control—to identify items. As a result, you can easily locate an item based on its content but not so easily locate an item based on its position. No workaround exists to make arrays support customizable keys; however, you can use a workaround to make dictionaries behave like indexed arrays.
To begin, let's walk through a dictionary and enumerate all the items' values and keys. You can use a For Each...Next statement such as
- For Each elem In dict
- MsgBox elem & " - " _
- & dict(elem)
where elem is a variable that represents each item's key (not its value). For each item in the dictionary, you display the key and the value concatenated together with a hyphen in between. To retrieve the key, you specify elem. To retrieve the value, you use the abbreviated Item property; however, instead of hard-coding the key, you specify the elem variable as the argument.
You can rewrite this For Each...Next statement so that it uses indexes, as Listing 2 shows. In Listing 2, you begin by creating an array called arr. Next, you use the Dictionary object's Items method, which fills an array with all the values in a dictionary. The Items method has the syntax
Including the parentheses after Items is optional. However, as you'll see shortly, including them is a good habit to get into.
In Listing 2, you're using the Items method to fill arr with the values in dict. After filling the array, you use the For...Next statement to traverse it and display all the values in one message box. Because you might not know how many values are in the array, you use the Count property to read the number of items that the Dictionary object has. By subtracting 1 from that number, you get the array's upper bound.
Because the Items method returns an array filled with all a dictionary's values, you can use this method to access a dictionary's content through indexes. You just need to specify which index you want to access with the syntax
where i is a variable index representing the index of the dictionary item you want to access. For example, if you want to access and display the first item, you specify
- MsgBox dict.Items()(0)
Thus, you can access an item in a dictionary without knowing the item's key. However, when you use indexes, you must remember to specify the index number in the second set of parentheses. If you use the code
- MsgBox dict.Items(0)
you'll receive an error, which is why including the empty parentheses after the Items method is a good habit to get into.
Instead of filling an array with a dictionary's values, you can fill an array with a dictionary's keys. You use the Keys method, which has the syntax
If you want to use an index to access a dictionary item's key, you use the code
where i is a variable index representing the index of the dictionary item for which you want to specify the key.
Deleting Items and Changing Keys
To delete an item, you use the Remove method, which takes the key of the item you want to delete as its sole argument. For example, if you want to remove the key-value pair of Italy-Rome, you specify
- dict.Remove ("Italy")
When you remove an item, all the items after the one you removed shift up one position. In addition, the value of the Count property changes.
The Remove method accepts only keys as arguments. If you want to remove the ith item from a Dictionary object, you need to use a workaround that uses the Remove and Keys methods:
- dict.Remove dict.Keys()(i)
where i is a variable index representing the index of the dictionary item you want to delete. If you want to delete all the items in a dictionary, you use the RemoveAll method. As the syntax
shows, RemoveAll doesn't take any arguments. Using RemoveAll is significantly faster than removing each item individually, especially if the dictionary has a lot of items.
If you want to change a key in a key-value pair, you don't need to delete the item then add a new one. Instead, you can use the Key property to change the key. This property has the syntax
- dObject.Key(key) = newkey
where key is the key you want to change and newkey is the key you want to change it to.
You can't change a value in a key-value pair. If you want a different value, you need to delete the item, then add a new one.
A Powerful Tool
The Dictionary object is powerful, much like Perl's associative array and Visual Basic for Applications' (VBA's) Collection object. Although the Dictionary object supports keys but not indexes, you can use a simple workaround so that you can use keys and indexes to access the data in dictionaries.
You can use the Dictionary object to write Windows script components that need to return structured data. However, you're not restricted to the Windows environment. Because Microsoft created the dictionary as a COM object, you can use it in any development environment that is COM-compliant.