Can you declare jagged arrays in excel VBA directl

2020-02-11 10:12发布

问题:

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.

回答1:

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 Integers.
  • 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:

  • Flexible

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. Collections 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


回答2:

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!



回答3:

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


标签: arrays excel vba