I have an array like this:
Dim aFirstArray() As Variant
How do I clear the entire array?
What about a collection?
You can either use the Erase or ReDim statements to clear the array. Examples of each are shown in the MSDN documentation. For example:
Dim threeDimArray(9, 9, 9), twoDimArray(9, 9) As Integer
Erase threeDimArray, twoDimArray
ReDim threeDimArray(4, 4, 9)
To remove a collection, you iterate over its items and use the Remove method:
For i = 1 to MyCollection.Count
MyCollection.Remove 1 ' Remove first item
Next i
For deleting a dynamic array in VBA use the instruction Erase.
Example:
Dim ArrayDin() As Integer
ReDim ArrayDin(10) 'Dynamic allocation
Erase ArrayDin 'Erasing the Array
Hope this help!
It is as simple as :
Erase aFirstArray
Find a better use for myself:
I usually test if a variant is empty, and all of the above methods fail with the test. I found that you can actually set a variant to empty:
Dim aTable As Variant
If IsEmpty(aTable) Then
'This is true
End If
ReDim aTable(2)
If IsEmpty(aTable) Then
'This is False
End If
ReDim aTable(2)
aTable = Empty
If IsEmpty(aTable) Then
'This is true
End If
ReDim aTable(2)
Erase aTable
If IsEmpty(aTable) Then
'This is False
End If
this way i get the behaviour i want
[your Array name] = Empty
Then the array will be without content and can be filled again.
ReDim aFirstArray(0)
This will resize the array to zero and erase all data.
i fell into a case where clearing the entire array failed with dim/redim :
having 2 module-wide arrays, Private inside a userform,
One array is dynamic and uses a class module, the other is fixed and has a special type.
Option Explicit
Private Type Perso_Type
Nom As String
PV As Single 'Long 'max 1
Mana As Single 'Long
Classe1 As String
XP1 As Single
Classe2 As String
XP2 As Single
Classe3 As String
XP3 As Single
Classe4 As String
XP4 As Single
Buff(1 To 10) As IPicture 'Disp
BuffType(1 To 10) As String
Dances(1 To 10) As IPicture 'Disp
DancesType(1 To 10) As String
End Type
Private Data_Perso(1 To 9, 1 To 8) As Perso_Type
Dim ImgArray() As New ClsImage 'ClsImage is a Class module
And i have a sub declared as public to clear those arrays (and associated run-time created controls) from inside and outside the userform like this :
Public Sub EraseControlsCreatedAtRunTime()
Dim i As Long
On Error Resume Next
With Me.Controls 'removing all on run-time created controls of the Userform :
For i = .Count - 1 To 0 Step -1
.Remove i
Next i
End With
Err.Clear: On Error GoTo 0
Erase ImgArray, Data_Perso
'ReDim ImgArray() As ClsImage ' i tried this, no error but wouldn't work correctly
'ReDim Data_Perso(1 To 9, 1 To 8) As Perso_Type 'without the erase not working, with erase this line is not needed.
End Sub
note : this last sub was first called from outside (other form and class module) with Call FormName.SubName but had to replace it with Application.Run FormName.SubName , less errors, don't ask why...
Only use Redim statement
Dim aFirstArray() As Variant
Redim aFirstArray(nRows,nColumns)
Related
I have trouble finding examples for this specific question.
I'm automating a task in Excel and I need users to paste a list of id-numbers in an areabox. When they click ok, I need my macro to get this list in an array so I can loop trough these id's and work with them (I want to check the formats, then paste the correct once in a column in Excel)
I tried and added a RefEdit on a userform, (multiline true, scrollbars both)
I've added this to be launched when click ok:
Dim data As Variant
Dim elemnt As Variant
data = Split(Simcards.simcardsArea.Text, vbNewLine)
For Each element In data
MsgBox element
Next element
Is there a better tool for this usage? Or is this the way to go?
I need the user to be able to paste the list of id's from a copy of any program, Excel, notepad, e-mail,..
Thank you
Option Explicit
Sub TestMe()
Dim arrayRange As Range
Set arrayRange = Application.InputBox("Enter a range", "Range:", Type:=8)
Dim myArr As Variant
Dim size As Long: size = arrayRange.Rows.Count - 1
ReDim myArr(size)
Dim myCell As Range
Dim myRow As Long: myRow = 0
For myRow = 0 To size
myArr(myRow) = arrayRange.Cells(myRow + 1, 1)
Next
Dim myVal As Variant
For Each myVal In myArr
Debug.Print myVal
Next myVal
End Sub
The trick is to pay attention how the array is assigned. The idea is that the array should be as big as the size number of the rows in the selected area, hence:
Dim size As Long: size = arrayRange.Rows.Count - 1
ReDim myArr(size)
The -1 is needed, because the arrays start from 0, unless someone writes Option Base 1 on the top of our code and break anything we have hoped for.
As arrays start with a 0, it is good to loop this way:
For myRow = 0 To size
myArr(myRow) = arrayRange.Cells(myRow + 1, 1)
Next
Problem: I am comparing two columns of names. If a name from the primary column matches a name in the secondary column, then I would like to add the matching name to an array of strings.
Function 1: This boolean function should indicate whether there is a match:
Function Match(name As String, s As Worksheet, column As Integer) As Boolean
Dim i As Integer
i = 2
While s.Cells(i, column) <> ""
If s.Cells(i, column).Value = name Then
Match = True
End If
i = i + 1
Wend
Match = False
End Function
Function 2: This function should add the matching name to a dynamic array of strings. Here I am somewhat stuck as I am new to arrays- any suggestions?
Function AddToArray(ys) As String()
Dim a() As String
Dim size As Integer
Dim i As Integer
Dim sh As Worksheet
Dim rw As Range
size = 0
ReDim Preserve a(size)
For Each rw In sh.Rows
If Match(sh.Cells(rw.Row, 1), s, column) = True Then
??
size = size + 1
End Function
Here is one solution. I scrapped your Match function and replaced it with a Find function.
Option Explicit
Sub AddToArray()
Dim primaryColumn As Range, secondaryColumn As Range, matchedRange As Range
Dim i As Long, currentIndex As Long
Dim matchingNames As Variant
With ThisWorkbook.Worksheets("Sheet1")
Set primaryColumn = .Range("A1:A10")
Set secondaryColumn = .Range("B1:B10")
End With
'Size your array so no dynamic resizing is necessary
ReDim matchingNames(1 To primaryColumn.Rows.Count)
currentIndex = 1
'loop through your primary column
'add any values that match to the matchingNames array
For i = 1 To primaryColumn.Rows.Count
On Error Resume Next
Set matchedRange = secondaryColumn.Find(primaryColumn.Cells(i, 1).Value)
On Error GoTo 0
If Not matchedRange Is Nothing Then
matchingNames(currentIndex) = matchedRange.Value
currentIndex = currentIndex + 1
End If
Next i
'remove unused part of array
ReDim Preserve matchingNames(1 To currentIndex - 1)
'matchingNames array now contains just the values you want... use it how you need!
Debug.Print matchingNames(1)
Debug.Print matchingNames(2)
'...etc
End Sub
Extra comments
There is no need to create your own Match function because it already exists in VBA:
Application.Match()
WorksheetFunction.Match()
and as I mentioned above you can also achieve the same result with the Find function which is my preference here because I prefer the way you can check for no matches (other methods throw less convenient errors).
Finally, I also opted to restructure your code into one Sub rather than two Functions. You weren't returning anything with your AddToArray function which pretty much means by definition it should actually be a Sub
As I stated in a comment to the question, there are a couple of problems in your code before adding anything to the array that will prevent this from working, but assuming that this was caused by simplifying the code to ask the question, the following should work.
The specific question that you are asking, is how to populate the array while increasing its size when needed.
To do this, simply do this:
Instead of:
ReDim Preserve a(size)
For Each rw In sh.Rows
If Match(sh.Cells(rw.Row, 1), s, column) = True Then
Reorder this so that it is:
For Each rw In sh.Rows
If Match(sh.Cells(rw.Row, 1), s, column) = True Then
ReDim Preserve a(size) 'increase size of array
a(size) = sh.Cells(rw.Row,1) 'put value in array
size = size + 1 'create value for size of next array
End If
Next rw
....
This probably isn't the best way to accomplish this task, but this is what you were asking to do. First, increasing the array size EVERY time is going to waste a lot of time. It would be better to increase the array size every 10 or 100 matches instead of every time. I will leave this exercise to you. Then you could resize it at the end to the exact size you want.
Okay so a bit of context to this one. I have some code that sorts through a list of locations of parts, to create a dropdown list locations that can be selected and will be used elsewhere for sorting later on. The location is a 9 digit code and I am trying to use VLookup to find the corresponding name to its location code from a different sheet, Combining them into an array and then displays that array in a combo box.
I am currently getting a "Subscript out of range" error on the line containing the VLookup.
Locan2(i, 1) = Application.VLookup(Locan(i), Sheet16.Range("A2:D700"), 2, False)
I have included below the whole subroutine for context, any help would be appreciated this is annoying me greatly and I can't find anything similar from my searches.
Private Sub Compbox() '<============Populates Location Drop down list==============>
Dim lastrow As Long
Dim Locn As String
Dim Locan() As String
Dim Locan2() As String
Dim Location As Collection
lastrow = Sheet5.Cells(Sheet5.Rows.Count, "J").End(xlUp).Row 'Find length of Sheet5
Set Location = New Collection
For i = 4 To lastrow
Locn = Sheet5.Cells(i, 10).Value
On Error Resume Next
Location.Add (Locn), CStr(Locn) 'Add location values, ignore duplicates
On Error GoTo 0
Next i
i = 0
QuickSort Location, 1, Location.Count 'Sort into ascending order
CollectionToArray Location, Locan 'Turn into an array
ReDim Locan2(UBound(Locan), 2)
For i = 0 To UBound(Locan)
Locan2(i, 0) = Locan(i)
Locan2(i, 1) = Application.VLookup(Locan(i), Sheet16.Range("A2:D700"), 2, False)
Next i
'Find the corresponding location name and create a 2D array
With Me.ComboBox2
.ColumnCount = 2
.BoundColumn = 1 'display combo box
.ColumnWidths = "1 in; 3in"
.List = Locan
End With
End Sub
Thanks for your help in advance!
Perhaps there is some flaw in your logic in these steps:
QuickSort Location, 1, Location.Count
CollectionToArray Location, Locan 'Turn into an array
ReDim Locan2(UBound(Locan), 2)
Potentially an off-by-one error or maybe no allocated arr is being returned?
I notice you are using custom VBA functions to sort the collection and transform to Array.
Why not use built-in ArrayList object?
Dim Location As mscorlib.ArrayList
Dim Locan As Variant
Set Location = New mscorlib.ArrayList
For i = 4 To lastrow
Locn = Sheet5.Cells(i, 10).Value
On Error Resume Next
Location.Add CStr(Locn) 'ArrayList has the add function too!
On Error GoTo 0
Next i
Location.Sort 'Has the sort function built-in too!
Locan = Location.toArray 'And the toArray function is built-in too!!
That may assist you if you are doing standard quicksort and stand toArray functionality.
I left the full Object declaration so you can see you need to add a reference to the mscorlib.dll
You're getting the error because you're going past the last index of Locan2, I would put a breakpoint in on that line and take a look at the value of i. Difficult to say more without having the whole sheet in front of me.
I have a sub that creates an array and fills it with data, but I would like to use the same array (with the data recorded from previous sub) in a different sub/function. Is there a way to achieve that.
I'm quite new to VBA, so maybe I'm missing something obvious here.
Thanks in advance.
EDIT:
'//FIRST CODE
Dim MyResults() As String
'
'
'Fill MyResults() with data
'
'
Call ComboToText
'//SECOND CODE
Private Sub ComboToText()
'// If there is more than one item in MyResults() use combo box. For one item use Textbox.
Dim n As Long
If UBound(MyResults) > 1 Then
txtPCB.Visible = False
With cmbPCB
.Visible = True
For n = LBound(MyResults) To UBound(MyResults)
.AddItem MyResults(n)
Next n
.Style = fmStyleDropDownCombo
End With
Else
txtPCB.Text = MyResults(1)
End With
End Sub
Whenever I'm trying to run, VBA insists on declaration of MyResults() in second code too. If I declare it again, wouldn't I lose the data that's already in it?
As YowE3K says, you can pass the array as an argument to another sub:
Dim MyResults() As String
'Fill MyResults() with data
Call ComboToText(MyResults)
'//SECOND CODE
Private Sub ComboToText(MyResults() As String)
...
It's fine to use the same name in both Subs, as I did, but you can also use a different name in the second Sub.
I am trying to loop through a table that has a column for "customers" and "dollar amount". If my loop finds a customer called "greg" or "henry" I want to add his "dollar amount" to an array of an unknown size.
Can someone please help me?
If by unknown size, you mean that number of elements is unknown, you could use a dynamic array.
Dim aArray() As Single ' or whatever data type you wish to use
ReDim aArray(1 To 1) As Single
If strFirstName = "henry" Then
aArray(UBound(aArray)) = 123.45
ReDim Preserve aArray(1 To UBound(aArray) + 1) As Single
End If
Ubound(aArray) throws an error if the array hasn't been dimensioned, so we start by adding an element to it. That leaves us with an empty element at the end of the text, so your code should account for that. aArray(Ubound(aArray)-1) will give you the last valid element in the array.
Private Sub ArrayMy(DataRange)
Dim DataIndex() As String
i = 0
On Error Resume Next
ReDim DataIndex(0)
For Each c In DataRange
DataIndex(i) = c
i = i + 1
ReDim Preserve DataIndex(i)
Next
End Sub
I think is better to use the listArray object:
Dim list, name as variant
Set list = CreateObject("System.Collections.Arraylist")
For i = 1 to Last then ''Loop in the range
If strName = "Henry" then
list.Add ValueToAdd ''add to the list
End if
Next
and then you can loop in the list
For Each name in List
Msgbox name
Next