Joining two arrays in vba? - arrays

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

Related

Array to a range where the range has missed columns

I want to output an array to a range, however I want the range to be made up of several parts. My example code is below with the expected output.
Sub test()
a = Array(1, 2, 3, 4, 5)
Range("A2:B2,D2:F2") = a
End Sub
Output I Get:
A-B-C-D-E-F
1-2---1-2-3
Output I Want:
A-B-C-D-E-F
1-2---3-4-5
How do I achieve the output I need from the array? As you can see the output skips column C correctly but starts to output the array from the start again and not continue as I expected.
This isn't possible, sadly. You'll need to write code to write to a discontinuous Range the way you want. This has been discussed before on another site: https://www.mrexcel.com/board/threads/filling-a-non-contiguous-range-with-an-array-with-vba.763467/
This won't be valid for all situations of course, but if you know the sizes of the discontinuous ranges beforehand, you could store your info in multiple arrays that are appropriately sized, and then write to each range the data in the respective array. This way, you avoid the problem entirely.
If that's not possible, I would create a Sub that takes an array and all of the ranges as inputs, splits the array into as many appropriately-sized arrays as needed, and then write each of those arrays to the respective ranges.
Actually it does exactly what is expected.
If you want to fill non-continous ranges you need to do it with each area:
Sub test()
Dim Area1 As Variant
Area1 = Array(1, 2)
Range("A2:B2") = Area1
Dim Area2 As Variant
Area2 = Array(3, 4, 5)
Range("D2:F2") = Area2
End Sub
Late extension to the valid answers above allowing to automate the assignment of partial array values to a non-contiguous range; as said it is not possible to do this by a single assignment:
a) Automate assigning partial values
Sub testSlice()
'1. Define array values
Dim arr As Variant
arr = Array(1, 2, 3, 4, 5)
'2. Assign sliced array values to each range area
With Sheet1.Range("A2:B2,D2:F2") ' non contiguous range
Dim i As Long, col As Long
For i = 1 To .Areas.Count ' loop through each area
Dim cols As Long: cols = .Areas(i).Columns.Count
'Assign partial array
.Areas(i) = Slice(arr, col + 1, cols) ' << aux. function
col = col + cols ' provide for next col
Next i
End With
End Sub
Function Slice()
Slices the given array values into partial arrays according to the individual areas via Application.Index():
Function Slice(arr, ByVal startCol As Long, ByVal columnCount As Long)
'Purp: slice "flat" 1-dim array
'Note: Sequence function assumes vers. MS 365 !!
' (Index function returns 1-based values)
Slice = Application.Index(arr, Application.Sequence(1, columnCount, startCol))
End Function
Instead of Sequence() you can use a column evaluation for prior versions:
Function Slice(arr, ByVal startCol As Long, ByVal columnCount As Long)
'Purp: slice "flat" 1-dim array for non-365 versions
Slice = Application.Index(arr, Evaluate("Row(" & startCol & ":" & (startCol + columnCount - 1) & ")"))
Slice = Application.Transpose(Slice)
End Function
b) Alternative with single cell assignments
Sub test2()
Dim arr As Variant
arr = Array(10, 20, 30, 40, 50)
'non contiguous range (assumes a one-row range as in OP!)
Dim rng As Range
Set rng = Sheet1.Range("A2:B2,D2:F2")
Dim cell As Range, i As Long
For Each cell In rng
cell = arr(i) ' assign array vals to cell
i = i + 1 ' increment 0-based arr index
Next cell
End Sub

Find name of array

I have this code:
Array1 = Array("apple", "pear")
Array2 = Array("Dog", "Cat")
All_Arrays = Array(Array1, Array2)
For each item in All_Arrays
Debug.print item
Next item
I want to change it to print the variable names Array1 and Array2. Is this possible?
I'd use a Dictionary, keyed with the identifier names:
Dim Array1 As Variant
Array1 = Array("apple", "pear")
Dim Array2 As Variant
Array2 = Array("Dog", "Cat")
With New Scripting.Dictionary
.Add "Array1", Array1
.Add "Array2", Array2
Dim names As Variant
names = .Keys
Dim outer As Long
For outer = LBound(names) To UBound(names)
Dim k As String
k = names(outer)
Debug.Print k & ":"
Dim inner As Long
For inner = LBound(.Item(k)) To UBound(.Item(k))
Debug.Print vbTab & .Item(k)(inner)
Next
Next
End With
Output:
Array1:
apple
pear
Array2:
Dog
Cat
Yep, use nested arrays all the time...
For iArr = 0 to ubound(All_Arrays)
subArr = All_Arrays(iArr) '0 will be Array1, 1 will be Array2
'Then do whatever you want with subArr
next iArr
edit1: Oh, maybe you want the actual variable name? "Array_1" and "Array_2" as strings? That is not possible (to my knowledge) without explicitly handing back a string of the name. i.e.
All_Arrays_Names = Array("Array1", "Array2")
edit2: Maybe it is possible, but its certainly not trivial: Print a variable's name
An Array of Arrays aka Jagged Array
Option Explicit
Sub testJaggedArray()
Dim Array1 As Variant: Array1 = Array("apple", "pear")
Dim Array2 As Variant: Array2 = Array("Dog", "Cat")
Dim All_Arrays As Variant: All_Arrays = Array(Array1, Array2)
Dim i As Long
Dim k As Long
For i = LBound(All_Arrays) To UBound(All_Arrays)
For k = LBound(All_Arrays(i)) To UBound(All_Arrays(i))
Debug.Print All_Arrays(i)(k)
Next k
Next i
End Sub
Result in the Immediate Window (CTRL+G)
apple
pear
Dog
Cat
To get the same result you could alternatively do:
For i = LBound(All_Arrays) To UBound(All_Arrays)
Debug.Print Join(All_Arrays(i), vbLf)
Next i
Very basic alternative using a jagged array
An array is no object disposing of something like a .Name property, but you might build an array of arrays, aka as jagged array defining your own names therein.
The idea is to use a jagged array's first element(row) as container for the array names, whereas only the succeeding elements contain the relevant arrays. Instead of #MathieuGuindon 's valid dictionary approach, the names can be read in directly from the jagged array (e.g. via a help function Header() here).
Note that the array names in this example are referred to as ordinal numbers (1-based).
The following code intends to demonstrate an alternative approach giving a basic starting idea and could be changed individually covering more sophisticated needs. This might include error handling, accepting other array bases and functions as well as developping additional class methods or properties.
Sub testJagged()
Dim jagged: ReDim jagged(2)
jagged(0) = Array("Array1", "Array2")
jagged(1) = Array("Apple", "Pear")
jagged(2) = Array("Dog", "Cat")
Dim i As Long
For i = LBound(jagged) + 1 To UBound(jagged)
Debug.Print header(jagged, i) & ":"
Debug.Print vbTab & Join(jagged(i), vbNewLine & vbTab)
Next
'how to refer to array names and selected elements
Dim NamedElem
For Each NamedElem In jagged(0)
Debug.Print "all Elems of " & NamedElem & ": " & _
Join(getNamedArray(jagged, NamedElem), ",")
Debug.Print "1st Elem of " & NamedElem & ": " & _
getNamedArray(jagged, NamedElem)(0)
Next
End Sub
Help function Header()
Function header(arr, ByVal OrdinalHeaderNum As Long)
'Note: assumes zero-based headers in jagged array
header = arr(LBound(arr))(OrdinalHeaderNum - 1)
End Function
Help function getNamedArray()
Function getNamedArray(arr, ByVal ArrName As String)
Dim num As Variant
num = Application.Match(ArrName, arr(LBound(arr)), 0)
If IsNumeric(num) Then getNamedArray = arr(num)
End Function
Output in VB Editor's immediate window:
Array1:
Apple
Pear
Array2:
Dog
Cat
all Elems of Array1: Apple Pear
1st Elem of Array1: Apple
all Elems of Array2: Dog Cat
1st Elem of Array2: Dog

Combining Multiple Arrays in VBA

I am currently trying to combine 46 arrays in to a single array. I have scoured the internet, to no prevail and am hoping someone here can help. I did find the below page, but I need to be able to look through each element of the new array in a nested for loop, so using the method below doesn't quite get me to my end goal.
Excel vba - combine multiple arrays into one
Basically, I need to combine my set of 46 arrays in such a way that I can then loop through each element using a nested for loop. ie.
Set of arrays:
myArray1 = (1, 2, 3, 4)
myArray2 = (5, 6, 7)
myArray3 = (8, 9)
myArray4 = (10, 11, 12, 13, 14)
.
.
.
myArray46 = (101, 102, 103)
Combine them to form new array:
myNewArray = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14... 101, 102, 103)
Loop through in nested for loop to check each element against my main array:
For i = LBound(mainArray) to UBound(mainArray)
For j = LBound(myArray) to UBound(myArray)
If mainArray(i) = myArray(j) Then
'do something
End If
Next j
Next i
Any help and/ or guidance with this is greatly appreciated!
Since you write in your comments that your end goal is to create an array of unique elements, you might be best served using a dictionary, where you can test for uniqueness as you add each element to dictionary. Something like:
Option Explicit
Function uniqueArr(ParamArray myArr() As Variant) As Variant()
Dim dict As Object
Dim V As Variant, W As Variant
Dim I As Long
Set dict = CreateObject("Scripting.Dictionary")
For Each V In myArr 'loop through each myArr
For Each W In V 'loop through the contents of each myArr
If Not dict.exists(W) Then dict.Add W, W
Next W
Next V
uniqueArr = dict.keys
End Function
Sub tester()
Dim myArray1, myArray2, myArray3, myArray4, myArray5
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7, 8)
myArray3 = Array(9, 10, 11, 12, 13, 14)
myArray4 = Array(15, 16)
myArray5 = Array(1, 3, 25, 100)
Dim mainArray
mainArray = uniqueArr(myArray1, myArray2, myArray3, myArray4, myArray5)
End Sub
If you run Tester, you will see mainArray contains:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
25
100
Using your data this is how to create one array out of many:
Public Sub TestMe()
Dim myA, myB, myC, myD, myE
myA = Array(1, 2, 3, 4)
myB = Array(5, 6, 7)
myC = Array(8, 9)
myD = Array(10, 11, 12, 13, 14)
myE = Array(101, 102, 103)
Dim myCombine As Variant
Dim myNew() As Variant
Dim myElement As Variant
Dim myArr As Variant
Dim cnt As Long
myCombine = Array(myA, myB, myC, myD, myE)
For Each myArr In myCombine
For Each myElement In myArr
ReDim Preserve myNew(cnt)
myNew(cnt) = myElement
cnt = cnt + 1
Next
Next
For cnt = LBound(myNew) To UBound(myNew)
Debug.Print myNew(cnt)
Next cnt
End Sub
The "building" of the new array is facilitated through ReDim Preserve, which keeps the old values in the array whenver the dimension of the array changes. And if you want to do something with these arrays, you may use 3 nested loops (a bit slow) and have some check:
Dim cnt2 As Long
For cnt = LBound(myNew) To UBound(myNew)
For cnt2 = LBound(myCombine) To UBound(myCombine)
For Each myElement In myCombine(cnt2)
If myElement = myNew(cnt) Then
Debug.Print myElement & vbTab & " from " & vbTab & cnt2
End If
Next myElement
Next cnt2
Next cnt
This is what you get on the immediate window:
1 from 0
2 from 0
3 from 0
4 from 0
5 from 1
6 from 1
7 from 1
8 from 2
9 from 2
10 from 3
11 from 3
12 from 3
13 from 3
14 from 3
101 from 4
102 from 4
103 from 4
Alternate 'brick-by-brick' approach.
Option Explicit
Sub combineArrays()
Dim myArray1 As Variant, myArray2 As Variant, myArray3 As Variant
Dim myArray4 As Variant, myArray46 As Variant
ReDim mainArray(0) As Variant
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7)
myArray3 = Array(8, 9)
myArray4 = Array(10, 11, 12, 13, 14)
'...
myArray46 = Array(101, 102, 103)
mainArray = buildMainArray(myArray1, mainArray)
mainArray = buildMainArray(myArray2, mainArray)
mainArray = buildMainArray(myArray3, mainArray)
mainArray = buildMainArray(myArray4, mainArray)
mainArray = buildMainArray(myArray46, mainArray)
ReDim Preserve mainArray(UBound(mainArray) - 1)
Debug.Print Join(mainArray, ",")
End Sub
Function buildMainArray(arr As Variant, marr As Variant)
Dim i As Long
For i = LBound(arr) To UBound(arr)
marr(UBound(marr)) = arr(i)
ReDim Preserve marr(UBound(marr) + 1)
Next i
buildMainArray = marr
End Function
The issue with using Redim Preserve to combine arrays is it can be an expensive operation, since you're basically re-creating the array everytime it's called. Since you have 46 arrays you're combining, you may very well be waiting a while.
Instead, you can loop over the arrays to figure out the total number of elements you need, dimension out your master array, then loop over the arrays again to do the actual assignment/merging. Something like this:
' encapsulates code to determine length of an individual array
' note that because arrays can have different LBounds in VBA, we can't simply use
' Ubound to determine array length
Public Function GetArrayLength(anArray As Variant) As Integer
If Not IsArray(anArray) Then
GetArrayLength = -1
Else
GetArrayLength = UBound(anArray) - LBound(anArray) + 1
End If
End Function
Public Function CombineArrays(ParamArray arraysToMerge() As Variant) As Variant
' index for looping over the arraysToMerge array of arrays,
' and then each item in each array
Dim i As Integer, j As Integer
' variable to store where we are in the combined array
Dim combinedArrayIndex As Integer
' variable to hold the number of elements in the final combined array
Dim CombinedArrayLength As Integer
' we don't initialize the array with an array-length until later,
' when we know how long it needs to be.
Dim combinedArray() As Variant
' we have to loop over the arrays twice:
' First, to figure out the total number of elements in the combined array
' second, to actually assign the values
' otherwise, we'd be using Redim Preserve, which can get quite expensive
' because we're creating a new array everytime we use it.
CombinedArrayLength = 0
For i = LBound(arraysToMerge) To UBound(arraysToMerge)
CombinedArrayLength = CombinedArrayLength + GetArrayLength(arraysToMerge(i))
Next i
' now that we know how long the combined array has to be,
' we can properly initialize it.
' you can also use the commented code instead, if you prefer 1-based arrays.
ReDim combinedArray(0 To CombinedArrayLength - 1)
' Redim combinedArray(1 to CombinedArrayLength)
' now that the combinedarray is set up to store all the values in the arrays,
' we can begin actual assignment
combinedArrayIndex = LBound(combinedArray)
For i = LBound(arraysToMerge) To UBound(arraysToMerge)
For j = LBound(arraysToMerge(i)) To UBound(arraysToMerge(i))
combinedArray(combinedArrayIndex) = arraysToMerge(i)(j)
combinedArrayIndex = combinedArrayIndex + 1
Next j
Next i
' assign the function to the master array we've been using
CombineArrays = combinedArray
End Function
To use this function, you'd do something like the following:
Public Sub TestArrayMerge()
Dim myArray1() As Variant
Dim myArray2() As Variant
Dim myArray3() As Variant
Dim myArray4() As Variant
Dim combinedArray As Variant
myArray1 = Array(1, 2, 3, 4)
myArray2 = Array(5, 6, 7)
myArray3 = Array(8, 9)
myArray4 = Array(10, 11, 12, 13, 14)
combinedArray = CombineArrays(myArray1, myArray2, myArray3, myArray4)
If IsArray(combinedArray) Then
Debug.Print Join(combinedArray, ",")
End If
End Sub
Regarding your last bit, that you're using an inner loop to combine the values in your final combined array: Your inner loop doesn't need to start at LBound(myArray). For any value of i, you've already compared it to the elements before it (e.g., when i = 2, it's already been compared to the first element). So you really just need:
For i = LBound(combinedArray) To UBound(combinedArray) - 1
For j = i + 1 To UBound(combinedArray)
' do whatever you need
Next j
Next i
Perhaps this ...
'To determine if a multi-dimension array is allocated (or empty)
'Works for any-dimension arrays, even one-dimension arrays
Public Function isArrayAllocated(ByVal aArray As Variant) As Boolean
On Error Resume Next
isArrayAllocated = IsArray(aArray) And Not IsError(LBound(aArray, 1)) And LBound(aArray, 1) <= UBound(aArray, 1)
Err.Clear: On Error GoTo 0
End Function
'To determine the number of items within any-dimension array
'Returns 0 when array is empty, and -1 if there is an error
Public Function itemsInArray(ByVal aArray As Variant) As Long
Dim item As Variant, UBoundCount As Long
UBoundCount = -1
If IsArray(aArray) Then
UBoundCount = 0
If isArrayAllocated(aArray) Then
For Each item In aArray
UBoundCount = UBoundCount + 1
Next item
End If
End If
itemsInArray = UBoundCount
End Function
'To determine the number of dimensions of an array
'Returns -1 if there is an error
Public Function nbrDimensions(ByVal aArray As Variant) As Long
Dim x As Long, tmpVal As Long
If Not IsArray(aArray) Then
nbrDimensions = -1
Exit Function
End If
On Error GoTo finalDimension
For x = 1 To 65536 'Maximum number of dimensions (size limit) for an array that will work with worksheets under Excel VBA
tmpVal = LBound(aArray, x)
Next x
finalDimension:
nbrDimensions = x - 1
Err.Clear: On Error GoTo 0
End Function
'****************************************************************************************************
' To merge an indefinite number of one-dimension arrays together into a single one-dimension array
' Usage: mergeOneDimArrays(arr1, arr2, arr3, ...)
' Returns an empty array if there is an error
' Option Base 0
'****************************************************************************************************
Public Function mergeOneDimArrays(ParamArray infArrays() As Variant) As Variant
Dim x As Long, y As Long, UBoundCount As Long, newUBoundCount As Long
Dim tmpArr As Variant, allArraysOK As Boolean
UBoundCount = 0
allArraysOK = True
For x = LBound(infArrays) To UBound(infArrays)
If Not IsArray(infArrays(x)) Or Not nbrDimensions(infArrays(x)) = 1 Then
allArraysOK = False
Exit For
End If
UBoundCount = UBoundCount + itemsInArray(infArrays(x))
Next x
If allArraysOK Then
ReDim tmpArr(0 To UBoundCount - 1)
UBoundCount = 0
For x = LBound(infArrays) To UBound(infArrays)
For y = LBound(infArrays(x)) To UBound(infArrays(x))
tmpArr(UBoundCount) = infArrays(x)(y)
UBoundCount = UBoundCount + 1
Next y
Next x
newUBoundCount = itemsInArray(tmpArr)
If newUBoundCount = UBoundCount Then
mergeOneDimArrays = tmpArr
Else
mergeOneDimArrays = Array()
End If
Erase tmpArr
Else
mergeOneDimArrays = Array()
End If
End Function
If you are working with one-dimensional arrays you could use a collection instead. It is much better at handling dynamic sizing.
You can declare a collection and then add each of the elements in the arrays to it. Then you will have one large list with all of the values.
Dim coll As New Collection
coll.Add MyArray(j)
Here is a good to collections introduction:
https://excelmacromastery.com/excel-vba-collections/

VBA passing an array from a 2D array to a sub

I have a function which collects which months are ticked in a user form, containing checkboxes:
Function get_entries() As Boolean()
This returns a 2D boolean array(4, 11) representing 5 x 12 check boxes, which in turn represents months that are selected from a userform
In my main function:
Dim montharr() As Boolean
montharr = get_entries()
Call myfunc1(montharr(0))
Call myotherfunc(montharr(1))
Call myotherfunc(montharr(2))
Call myotherfunc(montharr(3))
Call myotherfunc(montharr(4))
I can't pass in the individual arrays of 12 elements to the subs successfully. I have tried declaring items as variants too but this isn't working and have spent ages trying to get this to work. Any thoughts welcome.
Here's one way to "slice" a 2-D array:
Sub ArraySlicing()
Dim arr(1 To 5, 1 To 5)
Dim slice
Dim x, y
Dim a As Application
For y = 1 To 5
For x = 1 To 5
arr(y, x) = "R" & y & ":C" & x
Next x
Next y
Set a = Application
'get first "column"
slice = a.Transpose(a.Index(arr, 0, 1))
Debug.Print Join(slice, ", ")
'get second "row" (note double transpose)
slice = a.Transpose(a.Transpose(a.Index(arr, 2, 0)))
Debug.Print Join(slice, ", ")
End Sub
Index() gives you a 2-d array - (x,1) or (1,x) - Transpose() will convert that to a 1-d array.

Print Dynamic Error Array to Sheet

I'm having troubles getting my Error array to print to a range. I'm fairly sure I'm resizing it incorrectly, but I'm not sure how to fix it. I created a test add which just added garbage data from columns A and B, but normally AddPartError would be call from within various Subs/Functions, and then at the end of the main script process the array should be dumped onto a sheet. Here are the relevant functions:
Sub testadd()
For Each i In ActiveSheet.Range("A1:A10")
Call AddPartError(i.value, i.Offset(0, 1))
Next i
tmp = PartErrors
PrintArray PartErrors, ActiveWorkbook.Worksheets("Sheet1").[D1]
Erase PartErrors
tmp1 = PartErrors
PartErrorsDefined = 0
End Sub
Sub PrintArray(Data As Variant, Cl As Range)
Cl.Resize(UBound(Data, 1), 2) = Data
End Sub
Private Sub AddPartError(part As String, errType As String)
If Not PartErrorsDefined = 1 Then
ReDim PartErrors(1 To 1) As Variant
PartErrorsDefined = 1
End If
PartErrors(UBound(PartErrors)) = Array(part, errType)
ReDim Preserve PartErrors(1 To UBound(PartErrors) + 1) As Variant
End Sub
Ok. I did a bit of checking and the reason this doesn't work is because of your array structure of PartErrors
PartErrors is a 1 dimensional array and you are adding arrays to it, so instead of multi dimentional array you end up with a jagged array, (or array of arrays) when you actually want a 2d array
So to fix this, I think you need to look at changing your array to 2d. Something like the below
Private Sub AddPartError(part As String, errType As String)
If Not PartErrorsDefined = 1 Then
ReDim PartErrors(1 To 2, 1 To 1) As Variant
PartErrorsDefined = 1
End If
PartErrors(1, UBound(PartErrors, 2)) = part 'Array(part, errType)
PartErrors(2, UBound(PartErrors, 2)) = errType
ReDim Preserve PartErrors(1 To 2, 1 To UBound(PartErrors, 2) + 1) As Variant
End Sub
and
Sub PrintArray(Data As Variant, Cl As Range)
Cl.Resize(UBound(Data, 2), 2) = Application.Transpose(Data)
End Sub
NB. You also need to Transpose your array to fit in the range you specified.
You code is a little hard to follow, but redim clears the data that is in the array, so I think you need to use the "Preserve" keyword.
Below is some example code you can work through to give you the idea of how it works, but you will need to spend some time working out how to fit this into your code.
Good luck!
Sub asda()
'declare an array
Dim MyArray() As String
'First time we size the array I do not need the "Preserve keyword
'there is not data in the array to start with!!!
'Here we size it too 2 by 5
ReDim MyArray(1, 4)
'Fill Array with Stuff
For i = 0 To 4
MyArray(0, i) = "Item at 0," & i
MyArray(1, i) = "Item at 1," & i
Next
' "Print" data to worksheet
Dim Destination1 As Range
Set Destination1 = Range("a1")
Destination1.Resize(UBound(MyArray, 1) + 1, UBound(MyArray, 2) + 1).Value = MyArray
'Now lets resize that arrray
'YOU CAN ONLY RESIZE THE LAST SIZE OF THE ARRAY - in this case 4 to 6...
ReDim Preserve MyArray(1, 6)
For i = 5 To 6
MyArray(0, i) = "New Item at 0," & i
MyArray(1, i) = "New Item at 1," & i
Next
'and let put that next to our first list
' "Print" data to worksheet
Dim Destination2 As Range
Set Destination2 = Range("A4")
Destination2.Resize(UBound(MyArray, 1) + 1, UBound(MyArray, 2) + 1).Value = MyArray
End Sub

Resources