In my project I work a lot with jagged arrays, i.e. arrays of which the elements are also arrays.
Up until know I only managed to define these arrays like this:
dim subarray(1 to 3) as Integer
dim MyArray(1 to 5) as Variant
subarray(1) = 40
subarray(2) = 50
subarray(3) = 60
MyArray(1) = subarray
But I would like to do something like this:
dim MyArray(1 to 5)(1 to 3) as Variant/Integer
MyArray(1)(1) = 40
The example above doesn't compile. Is there a similar, valid way to declare nested arrays directly?
EDIT: The right term is 'jagged array' not 'nested array'.
EDIT2: Edited the example values, to prevent confusion between indices and values.
There are a variety of ways in VBA to have collections of collections. All of them have benefits and drawbacks.
Multidimensional Arrays
Good:
- Simple syntax (only one variable)
- Type safety. All elements of a matrix of
Integer
are known and enforced to be Integer
s.
- Very fast array access
Bad:
- If there are large differences in the size of the inner arrays, a matrix will waste some space because there are unused "cells" in the matrix.
- You can only change the bounds of the last dimension with
ReDim Preserve
. So you can't add "columns" to a matrix without clearing all the data.
You declare multidimensional arrays by including multiple bounds separated by commas:
Dim intMatrix(0 to 2, 0 to 4) As Integer
You can dynamically increase the last dimension of a multidimensional array if you first declare the array without any bounds:
Dim intMatrix() As Integer ' Uninitialized dynamic array
ReDim intMatrix(0 to 4, 0 to 2) ' Initialize as a matrix
ReDim Preserve intMatrix(0 to 4, 0 to 3) ' Add another "row" to the matrix, preserving existing data
Jagged Arrays
Good:
Bad:
- You lose compile-time type safety
- They are a bit tricky / messy because of the nested structure
- It is awkward and expensive to resize the inner arrays
You can create jagged arrays be declaring an outer array of type Variant()
, and assigning other arrays to the elements of the outer array:
Dim outer() As Variant ' Dynamic, so new inner arrays can be added
Dim inner() As Integer ' Dynamic, so new elements can be added
ReDim outer(0 to 3)
ReDim inner(0 to 4)
outer(2) = inner
Lost compile-time type information
All the compiler "knows" about the outer array is that it can contain anything. So the following code will compile:
Set objWorksheet = outer(2)(3)
Although at runtime this will cause an error because the inner array at outer(2)
contains Integers
, not Worksheet
objects.
Awkward to resize
One of the benefits of jagged array is that the inner arrays can be of different sizes. However, you cannot directly resize an inner array. VBA just can't handle the syntax; the following doesn't compile:
ReDim Preserve outer(2)(0 to 5)
In order resize an inner array, you first have to assign the inner array to a separate variable, resize that variable, and then assign it back to the jagged array:
Dim tempInts() As Integer
tempInts = outer(2)
ReDim Preserve tempInts(0 to 5)
outer(2) = tempInts
The reason you have to reassign tempInts
back to the outer
array is that arrays use by-value semantics in VBA. That means when you assign an array to a variable (as in tempInts = outer(2)
, you copy the entire array. That can be very expensive if your array is long (say a few thousand elements), and even more expensive if your array contains strings, because every single string must also be copied.
Jagged Collections
Good:
- Simple syntax for adding and removing elements
- Just as flexible as jagged arrays
- Collections use by-reference semantics so assigning is cheap, and you can have multiple references to the same collection object
Bad:
- Like jagged arrays, there is no type safety
If you will be adding elements to your inner arrays frequently, it will be a lot easier to use Collection
objects instead of arrays. Collection
s do not enforce the data type of their elements, so this has the same drawbacks of using Variant
arrays -- but you must do that to use jagged arrays anyways.
Dim cAnimals As New Collection
' Let's add stats on the Cheetah
Dim cCheetah As New Collection
' Easy to add inner collections to the outer collection. Also, cCheetah refers
' to the same collection object as cAnimals(1).
cAnimals.Add cCheetah
' Easy to add items to inner collection.
' Working directly with the cCheetah collection:
For Each vMeasurment In GetMeasurements("Cheetah")
cCheetah.Add vMeasurement
Next
' Working on the same collection by indexing into the outer object
For i = 1 To cAnimals.Count
For j = 1 To cAnimals(i).Count
cAnimals(i)(j) = cAnimals(i)(j) * dblNormalizingFactor
Next
Next
As Joshua says: there is no specific VBA syntax for declaring jagged arrays directly.
But Jagged arrays follow the normal VBA rules for assignment: eg
Dim a as integer
dim v as variant
a=17
v=a
a=19
You don't expect V to now equal 19!
Array of Arrays:
Dim aa(), ax(), dd, x(), xx(), x2() ' all are " As Variant"
' Array of Arrays - Variant(0 To 2) with 3 Variant(0 To 2) ( 3 Variant/Integer each )
aa = Array( Array(1, 2, 3), Array(4, 5, 6), Array(7, 8, 9) )
aa(0)(0) = 0
' Array of "Excel" arrays - Variant(0 To 2) with 3 Variant(1 To 3) (3 Variant/Integer each)
ax = Array([{1,2,3}], [{4,5,6}], [{7,8,9}])
ax(0)(1) = 0
Another option is Collection of Collections, or Dictionary of Dictionaries:
Set dd = CreateObject("Scripting.Dictionary")
Set dd(2) = CreateObject("Scripting.Dictionary")
dd(2)(4) = 24
Some "Excel" rectangular array examples (because not a VBA type and works in Excel formulas too):
' "row" array starts at 1 - Variant(1 To 3) with 3 Variant/Integer each
x = [{1,2,3}]
x(1) = 0
' "column" array starts at 1, 1 - Variant(1 To 3, 1 To 1)
xx = [{1;2;3}]
xx(1, 1) = 0
' "Excel" rectangular array - Variant(1 To 3, 1 To 3)
x2 = [{1,2,3;4,5,6;7,8,9}]
x2(1, 1) = 0
Stop ' pause to check the types in the Locals window