Last month, I discussed three basic syntax elements: variables, constants, and subtypes. However, I intentionally didn't discuss an important subtype: Array. This month, I take a close look at what Array variables, or simply arrays, are and how you can create, fill, resize, traverse, erase, split, and join them. I also cover multidimensional and nested arrays.
Related: Understanding VBScript: Operators
Arrays in VBScript
Unlike all the other subtypes I showed you last month, an array can hold more than one value. An array lets you address many data values through the same variable. Think of an array as a list (e.g., a list of usernames). You reference each item in this list with a common name and index number. The common name is the name of the array variable. The common name typically includes the prefix arr to denote that the variable contains data of the subtype Array. The index number is an integer subscript that denotes an item's relative location in the list. The indexes always increment sequentially, starting from 0. For example, if an array has three items, the indexes are 0, 1, and 2. The 0 index represents the first position, the 1 index represents the second position, and the 2 index represents the third position in the array.
You can create two types of arrays: static and dynamic. Static arrays stay a fixed size throughout their lifetime—that is, the index size remains constant. Thus, when you create a static array, you must know how many items the array will contain throughout its lifetime. If you don't know this information or you know that the array's index size will change, you need to create a dynamic array. Dynamic arrays don't have a fixed index size. You can increase or decrease the index size at any time.
Related: Sorting Arrays in VBScript
You can create arrays several ways, depending on whether they are static or dynamic. To create a static array, you use a Dim statement with the array's common name, such as
The number in parentheses specifies the maximum value of indexes in the array. The value of 2 denotes that the array's indexes range from 0 to 2, which means the array can contain up to three items. In other words, 2 is the array's upper bound. Arrays also have a lower bound. The lower bound of static (and dynamic) VBScript arrays is always 0 because arrays always start with the 0 index.
When you create a static array, the lower and the upper bound are 0 by default and the array is empty. Once you set a static array's upper bound, you can't change it because the static array's index size remains constant.
You can create a dynamic array in three ways:
- Use Dim without an upper bound. One way to create a dynamic array is to use a Dim statement in which you don't specify the array's upper bound. For example, to create the dynamic array arrUsers, you can use the statement
- Use ReDim alone. You can use ReDim without Dim to create a dynamic array that has an initial index size. For example, the code
- Use Array. You can use the Array function with Dim to simultaneously create and fill dynamic arrays. Dim creates and names the array, and Array fills its indexes. For example, to create arrUsers and assign the names of Mary, Bob, and Ann to its indexes, you use the code
However, this array is uninitialized, which means you won't be able to use it until you set its size. To set the array's size, you use a ReDim statement to set the upper bound. For example, if you want to set an upper bound of 2 for arrUsers, you specify
Although you set arrUsers' index size, the array is empty.
creates a dynamic array whose initial size is 3. The arrUsers array is empty.
You typically use ReDim when you already know a useful initial size for the dynamic array. Using ReDim is more direct and saves you from adding an extra line of code.
Dim arrUsers arrUsers = Array("Mary", "Bob", "Ann")
Note that the code doesn't explicitly mention the size of the array. VBScript determines the array's size by the number of arguments (e.g., "Mary" is an argument) that you pass to the Array function. Keep in mind that the returned array contains only those items that you pass as arguments.
Table 1 on the Win32 Scripting Journal Web site summarizes the options for creating dynamic and static arrays. Because the table mentions details such as whether you need to fill the array, you can use it as a quick reference guide.
If you don't use the Array function to create an array, the new array will be empty. To fill dynamic and static arrays, you assign items to the indexes in the following format:
arrGroc(0) = "bread" arrGroc(1) = "cereal" arrGroc(2) = "milk"
In this case, the value in the parentheses after the common name isn't the array's upper bound but rather the particular index that you're filling. For example, you're filling the 0 index with the "bread" data value. Figure 1 graphically depicts the filled arrGroc static array.
The items you're assigning to the arrGroc indexes are in quotation marks because the data's subtype is String. In this case, the array's indexes contain homogeneous data (i.e., data with the same subtype). An array's indexes can also contain heterogeneous data (i.e., data with different subtypes).
You can choose not to fill an index. If you leave an index empty, the array's index positions don't change. For example, if you leave the 0 index empty, it still remains in the first position.
Although leaving an index empty doesn't result in an error, assigning an item to an inappropriate index does. For example, in arrGroc, if you specify either of these lines
arrGroc(-1) = "fruit" arrGroc(3) = "vegetables"
you'll get an error message when you run the code. Using the -1 index produces an error because indexes always start from 0. You can't use negative indexes. The 3 index produces an error because you declared arrGroc as an array with an upper bound of 2. You can't use an index that exceeds the upper bound that you previously set.
To use an array's values, you can use the array's common name and the index of interest. For example, you can use the VBScript's MsgBox function to display an item in a dialog box. If you want to find the item in the first position of the arrGroc array, you use the code
which prompts MsgBox to display the result of "bread".
If you want to find out how many indexes an array contains, you can use VBScript's LBound and UBound functions, which return the lower and upper bounds, respectively, for a given array. To use these functions, you specify either LBound or Ubound, followed by the array's common name in parentheses. For example, if you want to display the lower and upper bound of arrGroc, you specify
MsgBox LBound(arrGroc) MsgBox UBound(arrGroc)
The result of MsgBox LBound is 0. The result of MsgBox UBound is 2.
When you use the Array function to create and fill a dynamic array, you must list all the items that the array currently contains. Thus, for this dynamic array to be useful in a real-world context, you must resize it as soon as you need to use it.
At any point in a script, you can use the ReDim statement to change a dynamic array's size or to initially set the size if you create an uninitialized dynamic array. For example, if you use the code
to create an uninitialized dynamic array at the beginning of your script, you can later set the array's size to 4 by specifying
You can also use ReDim to resize an array to a smaller dimension (e.g., go from an index size of 11 to 5). You can resize an array as many times as you want.
Whether the new index size is smaller or larger, ReDim reallocates the original array's memory block to the new sized array, which means you lose all information about the original array's indexes. For example, suppose you create arrUsers, set the initial size to 4, assign the 0 index to the username "Mary", and use MsgBox to display the item that the 0 index represents:
Dim arrUsers() ReDim arrUsers(3) arrUsers(0) = "Mary" MsgBox arrUsers(0)
At this point, MsgBox displays the result "Mary". However, if you resize arrUsers to 6 and again use MsgBox to display the item that the 0 index represents
ReDim arrUsers(5) MsgBox arrUsers(0)
the result is an empty display. Resizing the array to 6 causes the loss of all the original array's data.
Fortunately, you can prevent data loss by specifying the keyword Preserve after ReDim. When you increase an array's size, Preserve saves all array data. When you decrease an array's size, Preserve limits the loss of data to only those items no longer represented by the new indexes. For example, the code
ReDim Preserve arrUsers(5)
resizes arrUsers to 6 items and preserves all the existing items represented by indexes 0 through 5. So, if you apply Preserve to the previous example, the second use of MsgBox returns the name "Mary" rather than an empty display.
Filling Resized Arrays
When you increase a dynamic array's index size, you need to fill the new indexes. Although you use the familiar technique of assigning an item to each new index, you must indirectly reference that new index. Directly referencing it (i.e., using a constant value) defeats the purpose of dynamic arrays.
For example, suppose you use the Array function to create and fill arrUsers with "Mary", "Bob", and "Ann". You then decide to add "John" to arrUsers. If you specify
Dim arrUsers arrUsers = Array("Mary", "Bob", "Ann") ReDim Preserve arrUsers(3) arrUsers(3) = "John"
you're using a constant value (3) to set the array's new upper bound and index. A better approach is to use UBound to set the new upper bound and index:
Dim arrUsers arrUsers = Array("Mary", "Bob", "Ann") ReDim Preserve arrUsers(UBound(arrUsers) + 1) arrUsers(UBound(arrUsers)) = "John"
In the ReDim Preserve statement, UBound refers to the array's old size. You add a value of 1 to UBound because you're adding another index. You use UBound again to set the new index to "John". In this instance, UBound refers to the index number. You don't need to add 1 to UBound this time because the previous line already set arrUsers to the new value.
With static arrays, you always know the upper bound. Thus, you can easily traverse a static array because you know exactly how many steps you need to take to iterate through all its indexes. For example, suppose you want to display the items in the static array
Instead of using three MsgBox functions
MsgBox arrGroc(0) = "bread" MsgBox arrGroc(1) = "cereal" MsgBox arrGroc(2) = "milk"
to display each item in a separate message box, you can use one For...Next statement to display all the items in one message box. For...Next repeats a group of statements (in this case, the MsgBox line) a specified number of times. You use the lower and upper bounds to specify that number:
For i = 0 to 2 MsgBox arrGroc(i) Next
For...Next traverses arrGroc in three steps. The letter i is a variable index that represents the index MsgBox is displaying at any particular step. In other words, i represents the 0 index in the first step, the 1 index in the second step, and the 2 index in the third step.
Traversing dynamic arrays isn't as simple because you don't know the upper bound. To traverse dynamic arrays (and even static arrays), you can use LBound and UBound to dynamically return the lower and upper bounds for a given array. So, instead of specifying an array's lower and upper bounds, you use LBound and UBound:
For i = LBound(arrUsers) to UBound(arrUsers) MsgBox arrUsers(i) Next
Another technique you can use to traverse dynamic and static arrays applies VBScript's For Each...Next statement, which repeats a group of statements for each item in an array. Because this technique doesn't use index-based navigation, you don't have to deal with lower and upper bounds. This technique assigns all the items in an array to the elem variable. Once the items are in the variable, you can manipulate them. For example, to display all the items in arrUsers, you use the code
For Each elem In arrUsers MsgBox elem Next
If you need to free an array, you use the Erase statement. For static arrays, Erase doesn't change an array's lower and upper bounds; it erases only the items' content. Erase reinitializes an array's items by setting the array to the null value for the specified subtype (e.g., the empty string for strings, 0 for numbers, Nothing for objects). For example, if you run the code in Listing 1 that erases the static array, the message box displays "0 2". Thus, Erase didn't change the bounds. However, if you display the content of any item, you get only empty strings.
For dynamic arrays, Erase releases an array's allocated memory. For example, if you run the code in Listing 1 that erases the dynamic array, you get an error because Erase releases all the memory that refers to the array. The variable still exists, but the information about the bounds doesn't. Erase returns a dynamic array to the state immediately after its declaration when its bounds are uninitialized and unusable.
Splitting and Joining Array Indexes
VBScript's Split and Join functions let you split and join the values of indexes in a static or dynamic array. The Split function uses a delimiter to separate a string into substrings and assigns those substrings to an array's indexes. You specify the string and delimiter as arguments to the Split function. For example, if you pass the arguments of "One,Two,Three" and "," to the Split function in code such as
Dim arrNumbers arrNumbers = split("One,Two,Three", ",")
the function returns an array in which the 0 index represents "One", the 1 index represents "Two", and the 2 index represents "Three".
The Join function performs the reverse task. It combines the substrings that each array index contains into one string and assigns that string to a String variable (not an array—the prefix str denotes a variable whose subtype is String). For example, the code
Dim strNumbers strNumbers = join(arrNumbers, ",")
combines the substrings in the arrList indexes and assigns the result—the original string "One,Two,Three"—to strNumbers.
Using Multidimensional Arrays
All the arrays I've shown you so far contain one list of data, or dimension. However, arrays can contain more than one list of data. In other words, arrays can be multidimensional.
Figure 2 provides a graphical view of a two-dimensional array. Similar to a table, a 2-D array has a specific number of rows and columns. The first dimension forms the rows, and the second dimension forms the columns. Each cell represents an index.
You create static and dynamic 2-D arrays the same way you create 1-D arrays, except in 2-D arrays you have two upper bound values in the parentheses: the upper bound of the first dimension followed by the upper bound of the second dimension. For example, to create the 2-D static array, arrMultiGroc, in Figure 2, you specify
If you create a dynamic multidimensional array and you want to resize it with the ReDim Preserve statement, by design you can modify only the last dimension of the array. (If you use ReDim without Preserve, you can modify any dimension.) For example, suppose you create a dynamic 2-D array, arrMultiUsers, with the code
and later you want to resize it, preserving the original array indexes. If you specify
ReDim Preserve arrMultiUsers(2,2)
you'll get an error message because you changed the first dimension. If you specify
ReDim Preserve arrMultiUsers(3,4)
the code will successfully execute. If you specify
the code will execute, but you'll lose all the original index data because you didn't specify Preserve.
You manipulate the indexes of static and dynamic 2-D arrays the same way you manipulate the indexes of their 1-D counterparts. However, you must include the indexes of first and second dimensions, respectively, in parentheses. For example, to fill and display the 2,1 index of arrMultiGroc, you specify
arrMultiGroc(2,1) = "orange" MsgBox arrMultiGroc(2,1)
VBScript supports up to 60 dimensions. However, you'll probably never use more than three dimensions. If you're curious about what a 3-D array looks like, check out Figure A on the Win32 Scripting Journal Web site.
Using Nested Arrays
You can nest arrays, which means you fill an array's index with an existing array. Nested arrays aren't the same as multidimensional arrays, as Figure 3 shows. A multidimensional array has a somewhat flat structure that contains only one array. A nested array has a hierarchical structure that contains two or more independent arrays. The nested array in Figure 3 has three independent arrays. The arrChild2 array is nested in the arrChild1 array, which is nested in the main array.
Listing 2 shows how to nest an array. You first create and fill the array that you'll nest inside another array. In this case, that array is arrChild. You then assign arrChild as the third item (the 2 index) of the arrMain array. Note that arrMain contains heterogeneous data values.
The last line in Listing 2 displays the value of the index you just filled. As this line shows, you must use a special syntax to manipulate the items in a nested array. The code to address the first item of the child array is
Because a nested array isn't a multidimensional array, you can't use two conjunct indexes such as (2,0). Actually, you're just addressing the first item of a 1-D array called arrMain(2).
An Important Tool
Arrays are an important part of the VBScript language. Once you learn about VBScript operators, statements, and subroutines (which I'll cover in a future column), you can easily incorporate arrays into your scripts. (If you're already familiar with all aspects of VBScript, check out the Web exclusive sidebar "Using VBScript Arrays in Scripts." This sidebar provides an example of using VBScript arrays in a script and discusses why you might encounter problems if you use both VBScript and JScript arrays in a script. Go to the Win32 Scripting Journal Web site, and select View Current Issue in the Subscriber Areas. Select Using VBScript Arrays in Scripts from the July table of contents.)
Arrays aren't only easy to use but also powerful. Some of their many advantages include the following:
- You can use one variable to handle many data values, which lets you efficiently process numerous data values.
- You can easily enumerate the values in an array, which greatly facilitates applying the same code to a group of related data.
- Arrays can have more than one dimension, which helps considerably to model complex data.
- You can nest arrays, which lets you render not only tabular data sets but also hierarchical data sets
Learn more: Arrays and Temporary Files