Can I Use a Variable to ReDim an Array? - arrays

I am not entirely sure why I am getting the error message of
Expecting a dynamic array var
with this code:
Option Explicit
Sub ArrayTest()
Dim i As Integer, BankList(0) As Variant, x As Integer
For i = 0 To UBound(ScreenArray)
If ScreenArray(i) Like "TR=SUB*" Then
Debug.Print ScreenArray(i)
ReDim Preserve BankList(x) '<<< ERROR LINE
BankList(x) = ScreenArray(i)
x = x + 1 'Raise the value for the next occurrence, if needed.
End If
Next
End Sub
Basically I am attempting to move specific strings from one array to a new array, if certain criteria are met. It's difficult to determine how many strings will be in the new array until running this For...Next statement.
If you can't tell from the code, the original array is ScreenArray and the new array is BankList.

To create a dynamic array, do not specify the size in the original declaration.
So use BankList() As Variant instead of BankList(0) As Variant.

Related

Error 424 when trying to delete element in array vba [duplicate]

This question already has an answer here:
Search for an element in the VBA ArrayList
(1 answer)
Closed 2 years ago.
I was trying to delete the specific element in the array vba when certain condition are true but i end up getting error 424. May I know the right way to do it? I tired to use redim, however it doesn't suit my condition as after the comparing with others array i need to store the data back into excel file where the location in the excel file is already sorted.
Before changing the remarkRange to array variant, I used it as Dim remarkRange
As Range where I can just use .Clear to clear the range item in a specific element.
I tried remarkRange(I, 1)=" " it runs without error but im not sure if its suitable. May I know the correct way to do it? Thanks.
Dim remarkRange() As Variant
remarkRange= wb.Sheets("wb").Range("A1:A5").Value2
For I = LBound(remarkRange) To UBound(remarkRange)
If (some condition is true) then
remarkRange(I, 1).Delete
End If
Next I
I expected the element in the specific cell in the array to be empty, but I got error 424
An array doesn't have a Delete method. It's also misleading to have the Range in remarkRange when it's an array, not a Range. Maybe a different name, e.g. remarks or whatever is clear to you.
If you're going to write the array back to the worksheet, then I see no problem changing an element to a blank string.
For i = LBound(remarks, 1) To UBound(remarks, 1)
If some condition Then
remarks(i, 1) = ""
End If
Next i
It seems you'll need to decide what you mean by 'delete'. I'm not aware of a Delete property of an array of variants so while your code might compile it would throw an object required error.
However, your point about previously using the Clear method on a Range object, suggests that you just want to read your range values into an array, remove the contents if certain conditions aren't me, and then re-write your array to the range. If that's the case, you probably wouldn't want to resize your array as the rows or columns wouldn't line up - more commonly, you'd set the item of your variant array to Empty.
The code below shows how to do this in a simple routine of taking 10 numbers from column A, removing all odd numbers and re-writing the numbers to Column C - but with the rows still matching:
Public Sub EmptyItemsAndKeepArraySize()
Dim inArr() As Variant
Dim i As Long
'Read range into arrays.
inArr = Sheet1.Range("A1:A10").Value2
'Clear all numbers that are not even.
For i = 1 To UBound(inArr, 1)
If inArr(i, 1) Mod 2 <> 0 Then inArr(i, 1) = Empty
Next
'Write cleared array to column C
Sheet1.Range("c1").Resize(UBound(inArr, 1)).Value = inArr
End Sub
If, however, you really do want to remove and resize your array, then a simple way of doing it is to populate a temporary collection first, resizing an output array and then populating that with the collection items. In the example below the code removes all odd numbers and then writes the array to column B - but as an array reduced in size (ie contiguous rows):
Public Sub DeleteItemsAndShrinkArray()
Dim inArr() As Variant, outArr() As Variant
Dim i As Long
Dim temp As Collection
Dim v As Variant
'Read range into arrays.
inArr = Sheet1.Range("A1:A10").Value2
'Keep all even numbers in a temporary collection.
Set temp = New Collection
For i = 1 To UBound(inArr, 1)
If inArr(i, 1) Mod 2 = 0 Then temp.Add inArr(i, 1)
Next
'Dimension the output array.
ReDim outArr(1 To temp.Count, 1 To 1)
'Populate new array from temp collection.
i = 1
For Each v In temp
outArr(i, 1) = v
i = i + 1
Next
'Write reduced array to column B
Sheet1.Range("B1").Resize(UBound(outArr, 1)).Value = outArr
End Sub

VBA Insert value to array replacing value instead of inserting

I have a column of data with unique strings where the first 4 characters in the string may be a repeat of the first 4 characters in another string, in a format similar to:
ABCDEF
ABCDXY
ABCDKL
DTYTZF
DTYTSD
I am attempting to loop through this data to identify which 4 starting characters appear more then three times. If the first 4 digits of the string occur 3 times or more, I would like to remove these from the array entirely, and end up with an array that excludes these values. For example, in my column above, as 3 strings or more begin with 'ABCD', I would like to remove all strings that begin with this code, and have only every other value remain, such that my result would be:
DTYTZF
DTYTSD
I am currently looping through the array, pushing any value that occurs three times or more into a NEW array, and plan to then use that list to do a second pass on the original array, and remove any matches. This may not be the most efficient way, but I've not been able to determine a better way that is guaranteed not to mess my data up.
I have worked through looping through the strings to identify which strings occur more then once, but when I try to push them to an array, the string successfully is pushed to the array, but is then replaced with the next value as soon as it is pushed to the array. I know the value is pushed correctly, because if I view the array immediately afterwards, I see the value in the array. When the next value is pushed and you view the array again, only the new value is displayed (The older ones are not).
I believe this is due to my limited understanding of ReDim-ing arrays, and me not fully understanding a code snippet for pushing this value into an array. My (condensed) code is as follows:
Sub pickupValues()
Dim valuesArray()
Dim i As Long
Dim y As Long
Dim sizeCheck As Long
Dim tempArray() As String
valuesArray() = Worksheets("Sheet1").Range("A1:A10").Value
For i = LBound(valuesArray) To UBound(valuesArray)
sizeCheck = 0
For y = LBound(valuesArray) To UBound(valuesArray)
If Left(valuesArray(i, 1), 4) = Left(valuesArray(y, 1), 4) Then
sizeCheck = sizeCheck + 1
i = y
If sizeCheck >= 3 Then
ReDim tempArray(1 To 1) As String 'I'm not sure why I need to do this.
tempArray(UBound(tempArray)) = Left(valuesArray(i, 1), 4) 'I believe this is what pushes the value into the array.
ReDim Preserve tempArray(1 To UBound(tempArray) + 1) As String 'Again unsure on what the purpose of this is.
viewArray (tempArray)
End If
End If
Next y
Next i
End Sub
Function viewArray(myArray)
Dim txt As String
Dim i As Long
For i = LBound(myArray) To UBound(myArray)
txt = txt + myArray(i) + vbCrLf
Next i
MsgBox txt
End Function
What am I doing wrong?
I would like to re-use the same basic code later in the function to push other values OUT of an array based on if they match the string or not, but it seems VBA does not like to move values out of arrays either. Is there an easy solution that would match both scenarios?
I've rewritten what you are trying to do. I'm using the filter function to quickly get your results in the array
Option Explicit
Public Sub pickupValues()
Dim tmp As Variant
Dim results As Variant
Dim i As Long
Dim v
' Make sure this matches your range
With ThisWorkbook.Sheets("Sheet1")
' Important to transpose the input here as Filter will only take a 1D array. Even though it's only 1 column, setting an array this way will generate a 2D array
tmp = Application.Transpose(.Range(.Cells(1, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1)).Value2)
End With
' ReDiming to the maximum value and slimming down afterwards is much quicker then increasing your array each time you've found a new value
ReDim results(1 To UBound(tmp))
For Each v In tmp
' Less then 2 as first result is '0'. Will return '-1' if can't be found but as test criteria is in the array it will always be at least 0
If UBound(Filter(tmp, Left(v, 4))) < 2 Then
i = i + 1
results(i) = v
End If
Next v
' Redim Preserve down to actual array size
If i > 0 Then
ReDim Preserve results(1 To i)
viewArray (results)
Else
MsgBox "Nothing Found"
End If
End Sub
' Should really be a sub as doesn't return anything back to caller
Public Sub viewArray(myArray)
MsgBox Join(myArray, vbCrLf)
End Sub
Your algorithm is not helping you.
Option 1:
Sort your array. Then you can make a single pass to find sequential values with the same first four characters and count them.
Option 2:
Use a Dictionary object: first four characters as key, number of occurrences as value.

Add Strings to Dynamic Array VBA

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.

vba, Why an un-dimensioned array works with Split()

I can use an un-dimensioned String array with the Split() function to read fields from a String, but apparently, a String array has to be dimensioned to use it in a loop.
Why is that, and are there other situations where an array does not need to be dimensioned?
Dim field() As String
field = Split(data_line, "~")
Dim pref_line(10) As String
Input #1, pref_line(i)
but apparently, a String array has to be dimensioned to use it in a loop.
When use an un-dimensioned String array with the Split() function, the array is automatically dimensioned and values assigned to it. Also you can use that in a loop using For i = LBound(field) to UBound(field)
Option Explicit
Sub Sample()
Dim field() As String
Dim data_line As String
data_line = "aaa~bbb"
field = Split(data_line, "~")
Debug.Print field(0)
End Sub
Are there other situations where an array does not need to be dimensioned?
Yes. When you do not know how many items needs to be added to an array then you declare and un dimensioned array and Redim Preserve it to add values. For example
Option Explicit
Sub Sample()
Dim field() As Long
Dim n As Long, i As Long
ReDim Preserve field(n)
For i = 1 To 100
field(n) = i
n = n + 1
ReDim Preserve field(n)
Next i
End Sub
Split() returns its own array regardless of what you defined before. You are effectively overwriting the value of your variable.
Dim field() As String declares an array, but does not allocate it.
Dim pref_line(10) As String both declares and allocates it.
field = Split(data_line, "~") both allocates an array and populates it.
Others have already answered your question, and correctly about what you asked for. So I won't comment on that.
On a side note, you should avoid Redimming arrays as much as possible. Do that only when absolutely necessary. Remember that it is just one line of code for us, but a whole lot of work for the runtime engine. First find a new contiguous memory location for the new array; then copy the array items from the old to new one; then discard the old array. So you see one Redim Preserve statement produces how much work for the compiler.
For this reason, redimming inside a loop is "usually" a bad idea, since you know in advance how many times the loop will run and how many elements you will have in the end. So in such cases, redim your array before the start of loop, rather than doing it inside the loop. The end result would be a better performance :)
Taking the code in #SiddharthRout 's reply as an example, notice that the Loop will produce 100 items since you are looping from 1 to 100. So instead of ReDim Preserve field(n) inside the loop, move that outside it before the start of loop. ReDim Preserve field(100) as Long, or simply ReDim field(100) as Long.

How do I declare an array variable in VBA?

I need to add the var in array
Public Sub Testprog()
Dim test As Variant
Dim iCounter As Integer
If test = Empty Then
iCounter = 0
test(iCounter) = "test"
Else
iCounter = UBound(test)
End If
End Sub
Getting error at test(iCounter) = "test"
Please suggest some solution
Generally, you should declare variables of a specific type, rather than Variant. In this example, the test variable should be of type String.
And, because it's an array, you need to indicate that specifically when you declare the variable. There are two ways of declaring array variables:
If you know the size of the array (the number of elements that it should contain) when you write the program, you can specify that number in parentheses in the declaration:
Dim test(1) As String 'declares an array with 2 elements that holds strings
This type of array is referred to as a static array, as its size is fixed, or static.
If you do not know the size of the array when you write the application, you can use a dynamic array. A dynamic array is one whose size is not specified in the declaration (Dim statement), but rather is determined later during the execution of the program using the ReDim statement. For example:
Dim test() As String
Dim arraySize As Integer
' Code to do other things, like calculate the size required for the array
' ...
arraySize = 5
ReDim test(arraySize) 'size the array to the value of the arraySize variable
Further to Cody Gray's answer, there's a third way (everything there applies her as well):
You can also use a dynamic array that's resized on the fly:
Dim test() as String
Dim arraySize as Integer
Do While someCondition
'...whatever
arraySize = arraySize + 1
ReDim Preserve test(arraySize)
test(arraySize) = newStringValue
Loop
Note the Preserve keyword. Without it, redimensioning an array also initializes all the elements.
Further to RolandTumble's answer to Cody Gray's answer, both fine answers, here is another very simple and flexible way, when you know all of the array contents at coding time - e.g. you just want to build an array that contains 1, 10, 20 and 50. This also uses variant declaration, but doesn't use ReDim. Like in Roland's answer, the enumerated count of the number of array elements need not be specifically known, but is obtainable by using uBound.
sub Demo_array()
Dim MyArray as Variant, MyArray2 as Variant, i as Long
MyArray = Array(1, 10, 20, 50) 'The key - the powerful Array() statement
MyArray2 = Array("Apple", "Pear", "Orange") 'strings work too
For i = 0 to UBound(MyArray)
Debug.Print i, MyArray(i)
Next i
For i = 0 to UBound(MyArray2)
Debug.Print i, MyArray2(i)
Next i
End Sub
I love this more than any of the other ways to create arrays. What's great is that you can add or subtract members of the array right there in the Array statement, and nothing else need be done to code. To add Egg to your 3 element food array, you just type
, "Egg"
in the appropriate place, and you're done. Your food array now has the 4 elements, and nothing had to be modified in the Dim, and ReDim is omitted entirely.
If a 0-based array is not desired - i.e., using MyArray(0) - one solution is just to jam a 0 or "" for that first element.
Note, this might be regarded badly by some coding purists; one fair objection would be that "hard data" should be in Const statements, not code statements in routines. Another beef might be that, if you stick 36 elements into an array, you should set a const to 36, rather than code in ignorance of that. The latter objection is debatable, because it imposes a requirement to maintain the Const with 36 rather than relying on uBound. If you add a 37th element but leave the Const at 36, trouble is possible.
As pointed out by others, your problem is that you have not declared an array
Below I've tried to recreate your program so that it works as you intended.
I tried to leave as much as possible as it was (such as leaving your array as a variant)
Public Sub Testprog()
'"test()" is an array, "test" is not
Dim test() As Variant
'I am assuming that iCounter is the array size
Dim iCounter As Integer
'"On Error Resume Next" just makes us skip over a section that throws the error
On Error Resume Next
'if test() has not been assigned a UBound or LBound yet, calling either will throw an error
' without an LBound and UBound an array won't hold anything (we will assign them later)
'Array size can be determined by (UBound(test) - LBound(test)) + 1
If (UBound(test) - LBound(test)) + 1 > 0 Then
iCounter = (UBound(test) - LBound(test)) + 1
'So that we don't run the code that deals with UBound(test) throwing an error
Exit Sub
End If
'All the code below here will run if UBound(test)/LBound(test) threw an error
iCounter = 0
'This makes LBound(test) = 0
' and UBound(test) = iCounter where iCounter is 0
' Which gives us one element at test(0)
ReDim Preserve test(0 To iCounter)
test(iCounter) = "test"
End Sub
You have to declare the array variable as an array:
Dim test(10) As Variant
David, Error comes Microsoft Office Excel has stopped working. Two options check online for a solution and close the programme and other option Close the program
I am sure error is in my array but I am reading everything and seem this is way to define arrays.
The Array index only accepts a long value.
You declared iCounter as an integer. You should declare it as a long.

Resources