3

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.

JazZeus
  • 170
  • 2
  • 2
  • 14
  • Your example does not compile because does not follow the VBA rules (or the ones of any programming language I know): you cannot set two different types for the same variable. Additionally you are declaring a 2D array wrongly. Instead of trying the first combination of characters coming to your mind, you should do some research/learning (or even ask here: "how can I declare a 2D array?" -> kind of offtopic anyway); but what is the exact point of what you tried? Why you think that it will work? – varocarbas Oct 28 '13 at 11:54
  • Note that I'm looking for methods to declare _nested_ arrays, not _multi-dimensional_ arrays. I'm not sure if nested is the right word, but it is a different thing than a multi-dimensional array. – JazZeus Oct 28 '13 at 11:54
  • After reading your last comment, I am kind of curious: what was the exact point of what tried then... what you called nested array was done in your first code; what were you trying to do in the second bit of code? – varocarbas Oct 28 '13 at 11:56
  • 2
    Would a jagged array work? http://stackoverflow.com/a/9436309/2258 – Richard Morgan Oct 28 '13 at 11:58
  • @varocarbas I'm not trying yo declare a 2D-array. The first example does compile. MyArray is then declared as variant, and it's subarrays (in this case one) are declared as integer. I'm trying to achieve the same result in the second example, but without the excessive declaring of auxiliary variables. Note that this is possible in for instance Matlab. MyArray would then have the data type Cell, which can contain arrays on every position. – JazZeus Oct 28 '13 at 11:59
  • MatLab might (not sure, just because you are saying it) allow a shortcut under very specific conditions; but I don't think that you can assign two different types to the same variable. But still is not clear to me where are you planning to reduce from as far as your first code seems to be the minimal expression of what you need: you expect to populate automatically values to an array with its mere declaration? dim MyArray(1 to 5)(1 to 3) as Variant/Integer not only accounting for two types, but also understanding that with Integer, you meant "populate the array from 1 to 3" – varocarbas Oct 28 '13 at 12:03
  • @RichardMorgan The jagged array is indeed what I mean (I didn't know that term). The post uses it in the same way as my first example. I'm thus looking for a more direct way of declaring such an array and assigning values to it. – JazZeus Oct 28 '13 at 12:03
  • Ah!... I get (one of the points) where you are confused: dim subarray(1 to 3) as Integer does not tell the values in the array, but the indices (dimensions); just writing this line does not put anything in the array. One thing is dimensioning/instantiating/initialising an array and another things is populating it with values; doesn't MatLab follow this rule either? I guess that you are after an inline population. Example: Dim subarray: subarray = Array(1, 2, 3) – varocarbas Oct 28 '13 at 12:06
  • Right! I will edit the post to prevent the confusion. – JazZeus Oct 28 '13 at 12:07
  • Now what you want is clearer – varocarbas Oct 28 '13 at 12:14
  • I wrote an answer to your question (= rely on inline array-population) but, after reading Joshua Honig's one, I preferred to delete it because of not being sure about how recommendable is using jagged arrays at all (never use them personally, but no completely sure on this specific issue); and thus not being in a position to giving a truly-relevant advice (e.g., don't use them at all). – varocarbas Oct 28 '13 at 13:27

3 Answers3

13

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
Joshua Honig
  • 12,925
  • 8
  • 53
  • 75
  • In the fifth bullet, I believe you mean to say that you can’t add rows to a matrix without clearing all the data, no? – Mark E. Jan 26 '23 at 03:50
1

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
Slai
  • 22,144
  • 5
  • 45
  • 53
0

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!

Charles Williams
  • 23,121
  • 5
  • 38
  • 38
  • It is not odd that a primitive value use by-value semantics. It *is* odd that arrays themselves (the wrapper array object, not the individual elements) use by-value semantics. Try for example to create a `Class` module with a `Property` (not a field) of an array type. You will find every time you call the property get method you are indexing into a new array copy, and never change the values of the array "owned" by the class instance. – Joshua Honig Oct 28 '13 at 23:22
  • 1
    Why should arrays have different rules? Pointers yes, arrays of pointers yes, objects yes, arrays of objects yes, arrays of simple datatypes no. C works the same way. – Charles Williams Oct 28 '13 at 23:26