I am trying to basically do the equivalent of this in VBA:
myArray.apend((field1, field2, field3))
(using Python syntax)
So something where each element of the array/list has three elements. Can this be done in VBA?
To extend an array, use the ReDim statement:
Sub foo()
'## Declares your array of len==1
ReDim myArray(0)
myArray(0) = Array("A","B","C")
'## Extends your array:
ReDim Preserve myArray(Ubound(myArray)+1)
myArray(Ubound(myArray)) = Array("item1", "item2", "item3")
End Sub
Of course, since the item you've added is also an array, you could use the ReDim Preserve on the individual array items, as per cyboashu's answer, but this may be somewhat tedious/redundant.
Dim chld
i = UBound(myArray)
'Get a handle on the child array
chld = myArray(i)
'Extend it using ReDim Preserve
ReDim Preserve chld(UBound(chld) + 1)
'Add another value to the new item:
chld(UBound(chld)) = "another value"
'Reassign back to the parent array
myArray(i) = chld
You could also use the System.Collections.ArrayList object:
Sub f()
Dim myArrayList As Object
Dim i As Long
Set myArrayList = ArrayList
'Add ArrayList child objects to the ArrayList object:
myArrayList.Add ArrayList
i = myArrayList.Count - 1
'Add items to the child ArrayList:
myArrayList.Item(i).Add "A"
myArrayList.Item(i).Add "B"
myArrayList.Item(i).Add "C"
'Add some more:
myArrayList.Add ArrayList
i = myArrayList.Count - 1
myArrayList.Item(i).Add 1
myArrayList.Item(i).Add 2
myArrayList.Item(i).Add 3
'Dump this in to a VBA Array, if needed:
Dim myArray
myArray = myArrayList.ToArray()
End Sub
Function ArrayList()
Set ArrayList = CreateObject("System.Collections.ArrayList")
End Function
Screenshot of the .ToArray output in the Locals window:
Jagged Arrays:
Example:
Sub jaggedArray()
Dim arrMaster()
Dim arrChild()
Dim lCtr As Long
Dim lCtr2 As Long
For lCtr = 1 To 5
ReDim Preserve arrMaster(1 To lCtr)
For lCtr2 = 1 To 3
ReDim Preserve arrChild(1 To lCtr2)
arrChild(lCtr2) = "Child " & lCtr2
'/ Assing array in to array
arrMaster(lCtr) = arrChild
Next
Next
End Sub
Related
I have 2 arrays taken from 2 ranges in a sheet. I'm trying to create a third array that contains only the values contained in array 1 that are missing in array 2 (I found this code online).
Array 2´s size will vary and depends on this code:
Dim iListaIncompleta() As Variant
Dim iCountLI As Long
Dim iElementLI As Long
iCountLI = Range("B1").End(xlDown).Row
ReDim iListaIncompleta(iCountLI)
For iElementLI = 1 To iCountLI
iListaIncompleta(iElementLI - 1) = Cells(iElementLI, 2).Value
Next iElementLI
and Array 1's size is always from A1:A7, and I use this code to create it:
Dim iListaCompleta() As Variant
Dim iElementLC As Long
iListaCompleta = Range("A1:A7")
This is the original code I found online to extract missing values:
Dim v1 As Variant, v2 As Variant, v3 As Variant
Dim coll As Collection
Dim i As Long
'Original Arrays from the code:
v1 = Array("Bob", "Alice", "Thor", "Anna") 'Complete list
v2 = Array("Bob", "Thor") 'Incomplete list
Set coll = New Collection
For i = LBound(v1) To UBound(v1)
If v1(i) <> 0 Then
coll.Add v1(i), v1(i) 'Does not add value if it's 0
End If
Next i
For i = LBound(v2) To UBound(v2)
On Error Resume Next
coll.Add v2(i), v2(i)
If Err.Number <> 0 Then
coll.Remove v2(i)
End If
If coll.Exists(v2(i)) Then
coll.Remove v2(i)
End If
On Error GoTo 0
Next i
ReDim v3(LBound(v1) To (coll.Count) - 1)
For i = LBound(v3) To UBound(v3)
v3(i) = coll(i + 1) 'Collections are 1-based
Debug.Print v3(i)
Next i
End Sub
However, this code has arrays defined like this:
v1 = Array("Bob", "Alice", "Thor", "Anna")
And the actual arrays I wanna use are defined differently (as you can see in the first two pieces of code). When I try to run the code with them, it displays
Error 9: Subscript out of range.
The code works well as it originally is, but when I try to use MY arrays, it's when I get this error.
Obviously, I've tried it changing the names of the variables (v1 and v2) to my own 2 arrays (iListaCompleta and iListaIncompleta), and still doesn't work.
Any ideas??
Thank you in advance!
Here's a function that can be used to compare arrays of any dimension size to pull out differences and put only the differences in a one-dimensional array:
Public Function ArrayDifference(ByVal arg_Array1 As Variant, ByVal arg_array2 As Variant) As Variant
If Not IsArray(arg_Array1) Or Not IsArray(arg_array2) Then Exit Function 'Arguments provided were not arrays
Dim vElement As Variant
Dim hDifference As Object: Set hDifference = CreateObject("Scripting.Dictionary")
For Each vElement In arg_Array1
If Not hDifference.exists(vElement) Then hDifference.Add vElement, vElement
Next vElement
For Each vElement In arg_array2
If hDifference.exists(vElement) Then
hDifference.Remove vElement
Else
hDifference.Add vElement, vElement
End If
Next vElement
ArrayDifference = hDifference.Keys
End Function
Here's how you would call the function to compare two different arrays. It also includes how to populate the initial arrays using your provided setup:
Sub arrays()
Dim ws As Worksheet: Set ws = ActiveWorkbook.ActiveSheet
Dim rList1 As Range: Set rList1 = ws.Range("A1", ws.Cells(ws.Rows.Count, "A").End(xlUp))
Dim rList2 As Range: Set rList2 = ws.Range("B1", ws.Cells(ws.Rows.Count, "B").End(xlUp))
Dim aList1 As Variant
If rList1.Cells.Count = 1 Then
ReDim aList1(1 To 1, 1 To 1)
aList1(1, 1) = rList1.Value
Else
aList1 = rList1.Value
End If
Dim aList2 As Variant
If rList2.Cells.Count = 1 Then
ReDim aList2(1 To 1, 1 To 1)
aList2(1, 1) = rList2.Value
Else
aList2 = rList2.Value
End If
Dim aList3 As Variant
aList3 = ArrayDifference(aList1, aList2)
MsgBox Join(aList3, Chr(10))
End Sub
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
I have an array like this
dim arr(1 to 5) as string
arr(1)="a"
arr(3)="b"
arr(5) = "c"
(arr(2),arr(4) are empty).
How can I redim this arr(1to5) to exclude empty values and save also values "a","b","c" (I want the output like arr(1to3), arr(1)="a", arr(2)="b", arr(3)="c")?
In general I do not know how many of them will be empty, so I need some general code for this (not for this specific example).
I was thinking about new temporary array to save all nonempty values and then redim arr(1to5).
Maybe it is a better (quick) way to do it?
I wrote sth similar:
Sub test()
Dim myArray() As String
Dim i As Long
Dim y As Long
ReDim Preserve myArray(3)
myArray(1) = "a"
myArray(3) = "c"
Dim myArray2() As String
y = 1
For i = LBound(myArray) To UBound(myArray)
If myArray(i) <> "" Then
ReDim Preserve myArray2(y)
myArray2(y) = myArray(i)
y = y + 1
End If
Next i
ReDim myArray(UBound(myArray2))
myArray = myArray2
End Sub
However I would like to avoid creating new array.
create a new array of the same size. Loop the first array and insert the values when not empty into the new array keeping track of the last spot with value in the new array, then redim preserve the new array to only the size that has values.
Sub kjlkj()
Dim arr(1 To 5) As String
arr(1) = "a"
arr(3) = "b"
arr(5) = "c"
Dim newArr() As String
ReDim newArr(1 To UBound(arr))
Dim j As Long
j = LBound(newArr)
Dim i As Long
For i = LBound(arr) To UBound(arr)
If arr(i) <> "" Then
newArr(j) = arr(i)
j = j + 1
End If
Next i
ReDim Preserve newArr(LBound(newArr) To j - 1)
'do what you want with the new array.
End Sub
Alternative via Filter() function
"However I would like to avoid creating new array."
A negative filtering allows a basically simple alternative, however you have to
declare your array dynamically (i.e. without preset number of elements) to allow a rebuild overwriting the original array,
execute a double replacement over the joined array elements to allow insertion of a unique character that can be filtered out.
Sub testFilter()
Dim arr() As String
ReDim arr(1 To 5)
arr(1) = "a"
arr(3) = "b"
arr(5) = "c"
'Debug.Print Join(arr, ",") ' ~~> a,,b,,c
'rearrange arr via RemoveEmpty()
arr = RemoveEmpty(arr) ' >> function RemoveEmpty()
Debug.Print Join(arr, ",") ' ~~> a,b,c
End Sub
Help function RemoveEmpty()
Adding an unused unique character, e.g. $, to the empty elements plus eventual negative filtering allows to remove these marked elements.
Note that the double replacement is necessary to allow to mark consecutive empty elements by the $ mark, as VBA would skip additional characters here.
Function RemoveEmpty(arr)
Dim tmp
tmp = Replace(Replace(Join(arr, "|"), "||", "|$|"), "||", "|$|")
RemoveEmpty = Filter(Split(tmp, "|"), "$", False)
End Function
I'm trying to loop through a listbox and add the contents to an array....
My code is this:
Private Sub exportfolders_Click()
Dim list As String
Dim folderlist As String
Dim folderarray() As String
'Dim i As Interger
For i = 0 To Me.selectedfolders.ListCount - 1
'folderlist = (Me.selectedfolders.Column(0, i))
'folderarray() = Join(Me.selectedfolders.Column(0, i), ",")
list = (Me.selectedfolders.Column(0, i))
folderarray() = Join(list, ",")
ReDim Preserve folderarray(i)
Next i
folderlist = folderarray
'folderarray() = Join(folderlist, ",")
MsgBox (folderlist)
End Sub
You can see the bits I have commented out, trying all sorts to get it to work. But I keep getting the message "Can't assign to array" at folderarray(i) = Join(list, ","). Any pointers as to where I am failing?
You can concatenate the list box items into a string, and then use Split() to load your array. That way, the array is sized automagically without you needing to ReDim.
I tested this code in Access 2010:
Dim folderarray() As String
Dim i As Long
Dim strList As String
For i = 0 To Me!selectedfolders.ListCount - 1
strList = strList & "," & Me!selectedfolders.Column(0, i)
Next
' use Mid() to exclude the first comma ...
folderarray = Split(Mid(strList, 2), ",")
Note I don't know what you want to do with the array after loading it. MsgBox folderarray would throw Type mismatch error. MsgBox Mid(strList, 2) would be valid, but if that's what you want, you wouldn't need the array.
1) declare the array. Take a look at https://msdn.microsoft.com/en-us/library/wak0wfyt.aspx
2) No need of support variable
3) Assign the values to your array with the correct syntax
Private Sub exportfolders_Click()
Dim folderarray() As String
Dim i As Interger
Redim folderarray (Me.selectedfolders.ListCount-1)
For i = 0 To Me.selectedfolders.ListCount - 1
folderarray(i) = Me.selectedfolders.Column(0, i)
Next i
' Write here what you want to do with your array
End Sub
You could try something like this:
Private Sub ListToArray()
Dim folderArray() As Variant
Dim currentValue As String
Dim currentIndex As Integer
Dim topIndex As Integer
topIndex = Me.selectedfolders.ListCount - 1
ReDim folderArray(0 To topIndex, 0 To 1)
For i = 0 To topIndex
currentValue = Me.selectedfolders.Column(0, i)
folderArray(i, 0) = i
folderArray(i, 1) = currentValue
Next i
End Sub
Note my example is a multi-dimensional array which will give you the ability to add more than one item should you chose to do so. In this example I added the value of "i" as a placeholder/ index.
I have a list of 2 columns that A has a random number associated with it and B has a string item. So i was thinking I could make any Arraylist of arrays, but i would like to make the name of the Array the number so it can quickly be referenced. I've tried
Dim row As Integer
Dim arrList As Object
Dim arr As Variant
arrList = CreateObject("System.Collections.ArrayList")
row = 1
While Not IsEmpty(ActiveSheet.Range("A" & row))
ReDim arr(0 To 1) As String
arr(0) = ActiveSheet.Range("A" & row)
arr(1) = ActiveSheet.Range("B" & row)
arrList.Add (arr)
row = row + 1
Wend
Is there a quick way to search ArrList(arr(0)?
You probably should be using the Dictionary object (https://support.microsoft.com/en-us/kb/187234) and (Does VBA have Dictionary Structure?).
You can use .Exists to check if your key/item exists in the Dictionary or not.
Dim dctMyList As Dictionary
Dim nCount As Integer
Dim sKey As String
Set dctMyList = New Dictionary
For nCount = 1 To 5
sKey = "Fruit_" & nCount
dctMyList.Add sKey, Choose(nCount, "Apple", "Banana", "Date", "Fig", "Pear")
Next nCount
If dctMyList.Exists("Fruit_2") Then
Debug.Print dctMyList.Items(1)
Else
' Do something else
End If
Note that you can use .Items to access the contents, which is a zero-based index. So in the above example, "Banana" is the second item added to the Dictionary, but has an index of 1.