How to initialize a multidimensional array variable in vba for excel - arrays

The Microsoft site suggests the following code should work:
Dim numbers = {{1, 2}, {3, 4}, {5, 6}}
However I get a complile error when I try to use it in an excel VBA module.
The following does work for a 1D array:
A = Array(1, 2, 3, 4, 5)
However I have not managed to find a way of doing the same for a 2D array.
Any ideas?

You can also use a shorthand format leveraging the Evaluate function and a static array. In the code below, varData is set where [] is the shorthand for the Evaluate function and the {...} expression indicates a static array. Each row is delimited with a ; and each field delimited with a ,. It gets you to the same end result as simoco's code, but with a syntax closer to your original question:
Sub ArrayShorthand()
Dim varData As Variant
Dim intCounter1 As Integer
Dim intCounter2 As Integer
' set the array
varData = [{1, 2, 3; 4, 5, 6; 7, 8, 9}]
' test
For intCounter1 = 1 To UBound(varData, 1)
For intCounter2 = 1 To UBound(varData, 2)
Debug.Print varData(intCounter1, intCounter2)
Next intCounter2
Next intCounter1
End Sub

The Microsoft site suggests...
This suggestion is for VB.NET but not VBA.
For VBA you were in the right direction. You can do this:
Dim A as Variant
A = Array(Array(1, 2), Array(3, 4), Array(5, 6))

Alternative via Application.Index()
Extending on Dmitriv Pavliv's use of a jagged array (and as alternative to Robin Mackenzie's short hand approach), you can go a step further by applying Application.Index() on this array of arrays (with identical number of elements each) - note the double zero arguments!:
Sub Get2DimArray()
Dim arr() As Variant
'a) build array of arrays (aka as jagged array)
arr = Array(Array(1, 2, 4), Array(4, 5, 6), Array(7, 8, 9))
'b) make it 2-dimensional
arr = Application.Index(arr, 0, 0)
End Sub
Results in
a 2-dim arr(1 To 3, 1 To 3), where
* Row 1 ~> 1|2|4
* Row 2 ~> 4|5|6
* Row 3 ~> 7|8|9
Related link
Further reading regarding Some pecularities of the Application.Index() function

So here you generate the array without anything on it, just by telling its dimensions.
Dimension is X+1 because 0 counts as a position in the array.
Dim MyArray(X, X) As Integer
Then you fill it by doing for exemple
MyArray (0,0) = 1
MyArray (0,1) = 2
MyArray (1,0) = 3
MyArray (1,1) = 4
...
And so on.
If you want a more convenient way of filling it you can use For Cycles if there is a inherent logic to the numbers you are filling it with.

In case the size is unknown until run time.
Dim nRows As Integer, nCols As Integer
...
Dim yourArray() As Integer
ReDim yourArray(1 to nRows, 1 to nCols) 'One base initialisation
'ReDim yourArray(0 to nRows - 1, 0 to nCols - 1) 'Zero base initialisation
Then you can initialise (or access) the grid as:
yourArray(1, 1) = ... 'set first cell

Related

VBA Countif using Arrays as arguments

I was wondering if there is a possibility to use countif on arrays.
Currently there are two arrays. One is the Array with the Range (RangeArray) and the other the Criteria array (CritArray) which comes from another workbookbut is saved in an array. I'm trying to use the countif method in VBA using arrays if and store the countif values in a cell. So I don't need to loop between workbooks all the time.
Dim RangeArray, CritArray as Variant
RangeArray = Array(1,2,3,4,2,4,2,5,7,1,7,1,2)
CritArray = Array(1,2)
For i = 1 To LastRow
Cells(i, 1).Value = WorksheetFunction.CountIf(RangeArray, CriteriaArray)
Next i
When I try to do something amongst these lines it keeps giving the error "object required".
Any help would be kindly appreciated!
Kind regards,
Sub test()
Dim RangeArray, CritArray As Variant
Dim Counts As New Collection
RangeArray = Array(1, 2, 3, 4, 2, 4, 2, 5, 7, 1, 7, 1, 2, 11)
CritArray = Array(1, 2)
For i = 0 To UBound(CritArray)
Count = 0
For j = 0 To UBound(RangeArray)
If CritArray(i) = RangeArray(j) Then
Count = Count + 1
End If
Next
Counts.Add Count
Next
For k = 1 To Counts.Count
Cells(k, 1) = Counts(k)
Next
End Sub

Multiply 1D-array by 1D-array or Constant - VBA

At risk of being of topic, I decided to share some code, Q&A-style. If the general opinion is such that this would be off-topic I'll be happy to delete if need be.
Background:
I've been wondering if it was possible to return a 1D-array from multiplying another 1D-array by either a constant value or a third 1D-array (of the same size) without iteration.
So the process I'm looking for would look like:
Multiply by Constant > Derive {3,6,9} directly from {1,2,3}*3
Multiply by array > Derive {3,8,15} directly from {1,2,3}*{3,4,5}
Sample Code:
I have seen questions regarding this topic, but I've not yet seen an answer that would do this without iteration. The closest I've seen is from #SiddharthRout, on an external forum.
But usually one would opt for iteration:
Multiply by constant
Sub Test()
Dim arr1 As Variant: arr1 = Array(1,2,3)
Dim y As Long, x As Long: x = 3 'Our constant
For y = LBound(arr1) To UBound(arr1)
arr1(y) = arr1(y) * x
Next y
End Sub
Multiply by array
Sub Test()
Dim arr1 As Variant: arr1 = Array(1, 2, 3)
Dim arr2 As Variant: arr2 = Array(3, 4, 5)
Dim y As Long
For y = LBound(arr1) To UBound(arr1)
arr1(y) = arr1(y) * arr2(y)
Next y
End Sub
Question:
How could you retrieve a 1D-array from multiplying another 1D-array by any constant or another (equally sized) 1D-array without iteration?
As of what I found was that the key to the answer would lay in MMULT, returning an array from multiplying rows*columns.
Multiply 1D-Array by Constant
Sub Multiply_1D_byConstant()
Dim arr1 As Variant: arr1 = Array(1, 4, 3, 5, 10, 15, 13, 11, 6, 9)
With Application
Dim x As Long: x = 3 'Our constant
Dim y As Long: y = UBound(arr1) + 1
Dim arr2 As Variant: arr2 = .Evaluate("TRANSPOSE(ROW(" & x + 1 & ":" & x + y + 1 & ")-ROW(1:" & y + 1 & "))")
Dim arr3 As Variant: arr3 = .Evaluate("TRANSPOSE(ROW(1:" & y & "))")
Dim arr4 As Variant: arr4 = .Index(.MMult(.Transpose(arr1), arr2), arr3, 1)
End With
End Sub
Here, .Evaluate will quickly return a 1D-array n times our constant, n being Ubound(arr1)+1. In the above case: {3,3,3,3,3,3,3,3,3,3}
We than .Transpose arr1 within our .MMult(.Transpose(arr1), arr2) which will return a 2D-array. Because we would need to iterate that, we rather cut into the array to extract a 1D-array by .Index. The result of the above would be:
{3, 12, 9, 15, 30, 45, 39, 33, 18, 27}
To visualize how this works: .MMult will return a 2D-array from above example like so:
Then, because we basically give .Index an array like {1,2,3,4,5,6,7,8,9,10}, but in a dynamic way, for rows and just a 1 for the first column, .Index will slice a 1D-array out of this 2D-array:
Multiply 1D-Array by 1D-Array
This would work kind of the same. Let's imagine the below:
Sub Multiply_1D_by1D()
Dim arr1 As Variant: arr1 = Array(1, 4, 3, 5, 10, 15, 13, 11, 6, 9)
Dim arr2 As Variant: arr2 = Array(2, 1, 4, 1, 2, 3, 2, 5, 2, 1)
With Application
Dim y As Long: y = UBound(arr1) + 1
Dim arr3 As Variant: arr3 = .Evaluate("TRANSPOSE(ROW(1:" & y & "))")
Dim arr4 As Variant: arr4 = .Index(.MMult(.Transpose(arr1), arr2), arr3, arr3)
End With
End Sub
This time we don't tell .Index to extract the same, constant, first column from the result of .MMult, but we give it the same array of values as the rows. These values need to be a 1D-array so therefor we use the .Evaluate to return the array dynamically. So the above returns a 1D-array like:
{2, 4, 12, 5, 20, 45, 26, 55, 12, 9}
To visualize how this works: .MMult will return a 2D-array from above example like so:
Then, because we basically give .Index two arrays like {1,2,3,4,5,6,7,8,9,10}, but in a dynamic way, .Index will slice a 1D-array out of this 2D-array:
In this same fashion you can slice out any 1D-array from a 2D-array using .Index as long as you both specify the rows and columns parameter with a valid 1D-array. I hope this will be helpfull to anyone.

Joining two arrays in vba?

How do I combine these arrays with the outcome of (2, 4, 5, 3, 7, 6)?
array1 = Array(4,5,3,7,6)
array2 = Array(2)
You could potentially Join() and concatenate your two arrays, and then Split() the result back to a new array:
array3 = Split(Join(array2, ",") & "," & Join(array1, ","), ",")
Explanation:
Join() will return a string that has each element in the array (first parameter) delimited by a "," (second parameter). We concatenate those two joined arrays with one more comma to get a string like 2,4,5,3,7,6. We then use Split() to turn that string back into an array telling Split() that the delimter is a comma ",".
You could use arrayLists. This also provides for an easy sort if wanted.
Option Explicit
Public Sub test()
Dim list1 As Object, list2 As Object
Set list1 = CreateObject("System.Collections.Arraylist")
Set list2 = CreateObject("System.Collections.Arraylist")
list1.Add 4
list1.Add 5
list1.Add 3
list1.Add 7
list1.Add 6
list2.Add 2
list1.addRange list2
list1.Sort
End Sub
Joining two arrays
As an alternative to the correct and working approach proposed by Scott Craner
Create a third array that is empty the size of both arrays combined,
then loop through each array adding the items one by one.
... I demonstrate a way to
insert only the element(s) of the 2nd array by a loop
into a main array, whereas
the main array gets only restructured by a one liner via Application.Index().
As this function would change results to a 1-based array, I redimension the array back to a zero-based one. Furthermore I added an optional display in the VBE's Immediate Window resulting to 2|4|5|3|7|6 values:
1st step: Simple demo with same array values as in OP (Insertion of 1 element)
Sub SimpleDemo()
'[0]declare and assign zero-based 1-dimensioned arrays
Dim main, newTop
main = Array(4, 5, 3, 7, 6)
newTop = Array(2) ' only one element in a first step
'[1]transform main array by inserting(/i.e. repeating) "another" 1st element
main = Application.Index(main, Array(1, 1, 2, 3, 4, 5)) ' changes to 1-based 1-dim array
ReDim Preserve main(0 To UBound(main) - 1) ' back to zero-based 1-dim array
'[2]overwrite new first element by the 1st(only) element of newTop
main(0) = newTop(0)
'[3](optional) display in VBE's Immediate Window: main(0 To 5) ~> 2|4|5|3|7|6
Debug.Print "main(" & LBound(main) & " To " & UBound(main) & ") ~> " & _
Join(main, "|")
End Sub
2nd step: More generalized approach using a AddElem procedure
The above demo inserts only one element. Therefore I coded a AddElem procedure and a help function addedElems() to allow the insertion of more elements. Assumption is made that all 1-dim arrays are zero-based as in the original post; could be adapted easily btw :-)
Sub AddElem(main, newTop)
' Purp. : add/insert other array element(s) on top of zero-based main array
' Author: https://stackoverflow.com/users/6460297/t-m
' Date : 2020-02-05
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' a)insert newTop element(s) on top of main array
main = Application.Index(main, addedElems(main, newTop)) ' changes temporarily to 1-based mainay!
' b)make main array zero-based again (optional)
ReDim Preserve main(0 To UBound(main) - 1)
' c)overwrite inserted starting element(s) by the newTop element(s)
Dim i&: For i = 0 To UBound(newTop): main(i) = newTop(i): Next i
End Sub
Help function addedElems()
Function addedElems(main, newTop) As Variant()
'Note : help function called by AddElem()
'Purp.: return ordinal element counters of combined arrays
Dim i&, n&: n = UBound(main) + UBound(newTop) + 1
ReDim tmp(0 To n)
For i = 0 To UBound(newTop): tmp(i) = i: Next i
For i = i To n: tmp(i) = i - UBound(newTop): Next i
addedElems = tmp ' return combined elem counters, e.g. Array(1,2, 1,2,3,4,5)
End Function
Example call
I changed the values of OP's second array slightly (Array(2) ~>Array(20,21) to demonstrate the insertion of more elements, thus
resulting in a combined Array(20,21,2,4,5,3,7,6).
Sub ExampleCall()
'[0]declare and assign zero-based 1-dimensional arrays
Dim main, newTop
main = Array(4, 5, 3, 7, 6)
newTop = Array(20, 21)
'[1]Add/Insert newTop on top of main array
AddElem main:=main, newTop:=newTop ' or simply: AddElem main, newTop
'[2](optional) display in VBE's Immediate Window: ~~> main(0 To 6) ...20|21|4|5|3|7|6
Debug.Print "main(" & LBound(main) & " To " & UBound(main) & ") ..." & _
Join(main, "|")
End Sub
Related link
Similarly you can study some pecularities of the Application.Index() function applied on 2-dim arrays at Insert first column in datafield array without loops or API calls
Late to the party, but I'll also add my two cents
You could simply copy one of the two arrays into a new array. Then Redim Preserve that to be the size of the two original arrays to then loop only the first array. The following code is basic, but does the job quick without converting any data type:
Sub Test()
Dim arr1 As Variant: arr1 = Array(4, 5, 3, 7, 6)
Dim arr2 As Variant: arr2 = Array(2)
Dim arr3 As Variant: arr3 = arr2
ReDim Preserve arr3(UBound(arr1) + Ubound(arr2) + 1)
For x = (UBound(arr3) - UBound(arr1)) To UBound(arr3)
arr3(x) = arr1(x - UBound(arr2) - 1)
Next x
End Sub
To demonstrate the return of different Data Type using some Type conversions:
Sub Test()
Dim arr1 As Variant: arr1 = Array(CDbl(4), CLng(5), CStr(3), CDate(7), CCur(6))
Dim arr2 As Variant: arr2 = Array(2)
Dim arr3 As Variant: arr3 = arr2
ReDim Preserve arr3(UBound(arr1) + Ubound(arr2) + 1)
For x = (UBound(arr3) - UBound(arr1)) To UBound(arr3)
arr3(x) = arr1(x - UBound(arr2) - 1)
Next x
End Sub

How to split a constant 2D array initialization on several lines?

I need to create a 2D array which is not intended to be updated, only read.
Reading this question, I found this possibility using evaluate:
Dim varData As Variant
varData = [{1, 2, 3; 4, 5, 6; 7, 8, 9}]
In my case, the number of values requires to split the assignment on several lines, e.g.
varData = [{value1, value2; _
...; _
valueM, valueN}]
However when using _ to split this assignment, VBA complains at the first line with:
Compile Error:
Missing end bracket
I've tried to find the explanation, but all examples seems to use only a single line. What is wrong?
Note: I'm trying to populate an array, not cells in a sheet.
I was thinking of this.
Dim A as Variant
A = Array(Array(1, 2), _
Array(3, 4), _
Array(5, 6))

Redim Jagged Arrays VBA

Quick Question on Jagged Arrays. I have a static container array that will not change in size:
Dim StaticArray(1 to 3, 1 to 4, 1 to 12) as variant
I am assigning array values to each index in the static array as follows:
Dim ArrayInput() as Variant
ArrayInput = Array(1,2,3,4,5)
StaticArray(1,1,1) = ArrayInput
After assigning the array of values into StaticArray, I want the flexibility to add one more value to the ArrayInput Variable.
Is there any way to redim preserve the Variant contained in StaticArray(1,1,1)? Something like:
Redim Preserve StaticArray(1 to 3, 1 to 4, 1 to 12)(1 to ubound(?)+1)
Or is the only option to modify the ArrayInput variable and re-read?
Thanks!
I was wondering the same thing!
As you mention, I can only achieve this by creating a temporary array, redimensioning and then reassigning to the original jagged array. Here is my code:
Dim StaticArray(1 To 3, 1 To 4, 1 To 12) As Variant
Dim ArrayInput() As Variant
Dim TempArray() As Variant
ArrayInput = Array(1, 2, 3, 4, 5)
StaticArray(1, 1, 1) = ArrayInput
'Instead of redim, store array temporarily
TempArray = StaticArray(1, 1, 1)
'Redim the temporary
ReDim Preserve TempArray(UBound(TempArray) + 1)
'Asign value
TempArray(UBound(TempArray)) = 6
'Then store again on statick array
StaticArray(1, 1, 1) = TempArray

Resources