Array is blank when creating from entries in a column - arrays

This code creates an array off a range.
When I try to see if the array is saving the elements, by using Debug.Print, nothing is shown in the Immediate Window. It displays blank spaces. There are no errors.
This only happens in that part of the code, the first Debug.Print line works. I do have data in the columns.
Dim myArray() As Variant
Dim iCountLI As Long
Dim iElementLI As Long
If IsEmpty(Range("B3").Value) = True Then
ReDim myArray(0, 0)
Else
iCountLI = Sheets("Sheet1").Range("B3").End(xlDown).Row
iCountLI = (Range("B3").End(xlDown).Row) - 2
Debug.Print iCountLI
ReDim myArray(iCountLI)
For iElementLI = 1 To iCountLI
myArray(iElementLI - 1) = Cells(iElementLI + 2, 2).Value
Debug.Print myArray(iElementLI)
Next iElementLI
End If

Immediate problem: ReDim myArray(iCountLI) creates an array with empty values. In the For Loop, myArray(iElementLI - 1) = Cells(iElementLI + 2, 2).Value overwrites the first, second, etc. values, yet your Debug.Print myArray(iElementLI) is printing the second, third, etc. values. These, of course, haven't yet been overwritten, so they are still empty. The easiest fix, then, is to use Debug.Print myArray(iElementLI - 1).
More generally, I think you might be misunderstanding the meaning of ReDim myArray(iCountLI). Let's assume we have values in B3:B7. This would lead to ReDim myArray(5) in your code, but this is an array with 6 empty values, at location 0,1,2,3,4,5. This means you will keep an empty value trailing in the array after your loop, which is probably not what you want.
Here's a suggested rewrite with some comments:
Sub FillArray()
Dim myArray() As Variant
Dim iCountLI As Long
Dim iElementLI As Long
'let's assume: B3:B7 with values: 1,2,3,4,5
If IsEmpty(Range("B3").Value) = True Then
ReDim myArray(0, 0)
Else
'iCountLI = Sheets("Sheet1").Range("B3").End(xlDown).Row
'this line serves no purpose: you are immediately reassigning the value in the next line
'iCountLI = (Range("B3").End(xlDown).Row) - 2 'This would be 5,
'but myArray(5) would have SIX elements
iCountLI = (Range("B3").End(xlDown).Row) - 3
Debug.Print iCountLI '4
ReDim myArray(iCountLI)
'For iElementLI = 1 To iCountLI
For iElementLI = 0 To iCountLI '0 to 4
myArray(iElementLI) = Cells(iElementLI + 3, 2).Value 'starting at 0, so "+3", not "+2"
'myArray(iElementLI - 1) = Cells(iElementLI + 2, 2).Value
Debug.Print myArray(iElementLI) 'in succession: 1,2,3,4,5
Next iElementLI
End If
End Sub
Finally, it is worth pointing out that you don't actually need a For Loop to populate an array with values from a range. You could use something as follows:
Sub FillArrayAlt()
Dim myArray() As Variant
Dim iCountLI As Long
Dim iElementLI As Long
Dim myRange As Range
'let's assume: B3:B7 with values: 1,2,3,4,5
If IsEmpty(Range("B3").Value) = True Then
ReDim myArray(0, 0)
Else
Set myRange = Range("B3:" & Range("B3").End(xlDown).Address)
myArray = Application.Transpose(myRange.Value)
'N.B. Confusingly, used in this way, your array WILL start at 1!
For i = LBound(myArray) To UBound(myArray)
Debug.Print i; ":"; myArray(i)
' 1 : 1
' 2 : 2
' 3 : 3
' 4 : 4
' 5 : 5
Next i
End If
End Sub

You are valuing myArray(iElementLI -1) and printing myArray(iElementLI), which is still empty.

Related

Check if Value is in an Array and if it's not add it to the end

I'm trying to create an array with only unique values (Signal Names). For example my spreadsheet looks like this
Voltage
Voltage
Voltage
Current
Current
Current
etc....
I've got 32 signals however, I want this to work even if I don't know I have 32 signals explicitly i.e. 17 signals.
Signals("Voltage", "Current", "Etc....")
IN THE CODE BELOW
I realize I'm trying to ReDim an array within a loop and that's the problem. I'm just not able to think of another way of doing this. I would prefer to keep it as an array problem and not a dictionary or collection problem for now.
Public Sub Signals()
Dim myArray() As Variant
Dim Signals() As Variant
Dim element As Variant
Dim intA As Integer
WsName = ActiveSheet.Name
intRows = Sheets(WsName).Range("B2", Sheets(WsName).Range("B" & Sheets(WsName).Rows.Count).End(xlUp)).Rows.Count
intRows = intRows + 1
ReDim Signals(1)
Signals(1) = Sheets(WsName).Cells(4, 2).Value
For intA = 4 To intRows
For Each element In Signals()
If element <> Sheets(WsName).Cells(intA, 2) Then
ReDim Signals(UBound(Signals) + 1) 'This throws the error
Signals(UBound(Signals)) = Sheets(WsName).Cells(intA, 2).Value
End If
Next element
Next
End Sub
How the code doesn't work - RunTime Error '10' Array is temporarily fixed or locked.
I posted a solution to this issue using arrays in a similar question a couple days ago - using column B for your case, this would do the trick.
Aside from this solution, you have several problems in your current code - you're testing against each individual element in your current array without checking them all first, you're not using ReDim Preserve, and you need (0 to 0), not just a single (0) or (1). You're also naming your subroutine "Signals" while attempting to declare a variable "Signals" in the subroutine as well... That'll cause all kinds of issues.
Sub Test()
Dim list() As Variant
Dim inlist As Boolean
Dim n As Long, i As Long, j As Long, endrow As Long, colnum As Long
ReDim list(0 To 0)
inlist = False
j = 0
colnum = 2 'Column B in this case
endrow = Cells(Rows.Count, colnum).End(xlUp).Row
For n = 1 To endrow
For i = 0 To UBound(list)
If list(i) = Cells(n, colnum).Value Then
inlist = True
Exit For
End If
Next i
If inlist = False Then
ReDim Preserve list(0 To j)
list(j) = Cells(n, colnum).Value
j = j + 1
End If
inlist = False
Next n
For i = 0 To UBound(list)
Debug.Print list(i)
Next i
End Sub
Even simpler solution thanks to #user10829321's suggestions:
Sub Test()
Dim list() As Variant
Dim n As Long, i As Long, j As Long, endrow As Long, colnum As Long
ReDim list(0 To 0)
j = 0
colnum = 2 'Column B in this case
endrow = Cells(Rows.Count, colnum).End(xlUp).Row
For n = 1 To endrow
If IsError(Application.Match(Cells(n, colnum).Value, list, 0)) Then
ReDim Preserve list(0 To j)
list(j) = Cells(n, colnum).Value
j = j + 1
End If
Next n
For i = 0 To UBound(list)
Debug.Print list(i)
Next i
End Sub
An optional, if perhaps unwanted, solution using a scripting dictionary to give an array.
Public Function Signals(ByRef this_worksheet_range As excel.Range) As Variant()
Dim myArray() As Variant
Dim element As Variant
Dim interim_dic As Scripting.Dictionary
myArray = this_worksheet_range.values2
Set interim_dic = New Scripting.Dictionary
For Each element In myArray
If Not interim_dic.Exists(element) Then
interim_dic.Add Key:=element, Item:=element
End If
Next
Signals = interim_dic.Items
End Function

Excel VBA - Type mismatch when redim arr

I had an dynamic array where is value is from the worksheet.
If condition is meet, I would like to delete the element that meet the condition. The code was from this post but it is returning
Type Mismatch
on ReDim Preserve arr(Len(arr) - 1)
Sub arrtest()
Dim arr As Variant
Dim i As Integer
ReDim arr(1 To 1)
Dim cnt As Long
cnt = 0
For i = 1 To Cells(Rows.Count, "L").End(xlUp).Row
If Cells(i, "A").Value = "-2" Then
cnt = cnt + 1
ReDim Preserve arr(1 To cnt)
arr(cnt) = Cells(i, "A").Value
End If
Next i
For i = 1 To cnt
Debug.Print "This is arr: "; arr(i)
If arr(i) = "TEST" Then
Call DeleteElementAt(i, arr)
Debug.Print "This is new arr: "; arr(i)
Else
Debug.Print "Nothing is deleted"
End If
Next
End Sub
Public Sub DeleteElementAt(ByVal index As Integer, ByRef arr As Variant)
Dim i As Integer
' Move all element back one position
For i = index + 1 To UBound(arr)
arr(i - 1) = arr(i)
Next
' Shrink the array by one, removing the last one
ReDim Preserve arr(Len(arr) - 1)
End Sub
The Len (or Length) of an array is its Upper Boundary. To decrease the size of the array by 1,
ReDim Preserve arr(UBound(arr) - 1)
Conversely, the beginning of an array is the LBound or lower boundary. This is usually 0 (zero) or 1 (one) and in a two-dimensioned array you can specify the rank.

ReDim of one-dimensional array throws 'Subscript out of Range'

I want to redim an one-dimentional array by "cutting off" the first five entries as they have to be removed for a later logic.
I created a record set from a query
I filled an array recordSet() As Variant (size is 147).
Now: size of recordSet = size of daoRst3
I try to remove the first five elements of the array recordSet.
Code:
Set daoRst3 = gDB.OpenRecordset("SELECT * FROM TEST")
For i = 0 To daoRst3.Fields.Count - 1
ReDim Preserve recordSet(0 To i)
If daoRst3.Fields(i).Value = Empty Then
recordSet(i) = 0
Else: recordSet(i) = daoRst3.Fields(i).Value
End If
Next
'First five values in record set are not needed anymore.
ReDim Preserve recordSet(5 To i - 1)
The last row
ReDim Preserve recordSet(5 To i - 1)
throws "Subscript out of range". I already checked with debugging that i is 148 at this moment.
What might be the problem?
Many thanks in advance!
Alright this time i got it...
Private Sub this()
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT * FROM tmpCardList")
Dim msg As String
Dim myArray() As String
Dim array2() As String
ReDim array2(0 To rs.Fields.Count - 1)
Dim i As Long
For i = 0 To rs.Fields.Count - 1
Debug.Print ; rs.Fields(i).Name
array2(UBound(array2, 1) - i) = rs.Fields(i).Name
Next i
ReDim Preserve array2(UBound(array2) - 5)
ReDim myArray(0 To UBound(array2, 1))
For i = 0 To UBound(array2, 1)
myArray(UBound(array2, 1) - i) = array2(i)
Next i
rs.Close
Set rs = Nothing
End Sub
Turns out you have to write you own custom thing to sort the array. A little bit of an extra hoop. Just realized the final output is reversed but you could easily "undo" that by replicating my initial reversing logic.
Final Edit - this time with less typing - WOOHOO

How can I add empty spaces between values to a vba array?

I create an array in vba by looping through cells in a sheet (originalWS). So let's say cells (2,5) to (2,12) have the following:
(2,5)Item
(3,5)Type
(4,5)Nominal Diameter
(5,5)Lead
.
.
.
(12,5)For Use with End Blocks
Thus, when I loop with the code below I get an array that looks like this:
[Item,Type,Nominal Diameter,Lead,...,For Use with End Blocks].
However, I would like to add two empty spaces between each value in my array. so that it looks like this:
[Item,"","",Type,"","",Nominal Diameter,"","",Lead,"","",...,For Use with End Blocks,"",""]
ReDim propertyArr(1, lastRow)
For i = 1 To lastRow
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
Debug.Print propertyArr(1, i)
Next
I have tried to loop to by the final total size of the array so (lastRow*3) and step forward by 3. However, I'm having a hard time figuring out how I would reset my orginalWS.cells(i,5) values so that they are consecutive.
In other words, when I loop stepping by 3 my values would be:
propertyArr(1,1) = originalWS.Cells(2,5).value
propertyArr(1,4) = originalWS.cells(5,5).value
propertyArr(1,7) = originalWS.cells(8,5).value
How can I loop so that I store values in my array every 2 places, while I get the values from a consecutive list in a sheet.
Can I do this without having to add extra empty row
a way to add two empty spaces between each value within my original loop without having to add extra empty rows?
Or, can I add the two empty spaces between each value after I created my array the first time?
This should do the trick,
Dim lRowNo As Long
lRowNo = lastRow * 3
ReDim propertyArr(1, lRowNo)
For i = 1 To lRowNo
If i Mod 3 = 1 Then
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
Else
propertyArr(1, i) = ""
End If
Debug.Print propertyArr(1, i)
Next
Something like:
Sub ytrewq()
Dim propertyArr(), lastRow As Long
Dim originalWS As Worksheet
Set originalWS = ActiveSheet
lastRow = 5
ReDim propertyArr(1, 2 * lastRow)
For i = 1 To 2 * lastRow Step 2
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
propertyArr(1, i + 1) = ""
Debug.Print propertyArr(1, i)
Next
End Sub
UNTESTED
You can also unroll the loop a bit to do this a bit more efficiently. Note that for each iteration, i isn't incremented by 1, but by 3.
Public Sub test()
Dim lastRow As Long
lastRow = 6
Dim lastIndex As Long
lastIndex = lastRow * 3
ReDim propertyArr(1 To lastIndex)
Dim i As Long
For i = 1 To lastIndex Step 3
propertyArr(i) = CInt(i / 3)
propertyArr(i + 1) = vbNullString
propertyArr(i + 2) = vbNullString
Next
End Sub
Or without loops
Dim ws As Worksheet
Set ws = Sheets(1)
propertyarr = Join(Application.Transpose(ws.Range("E1:E5")), ","""","""",")
to put back into array
propertyarr = Split(Join(Application.Transpose(ws.Range("E1:E5")), ",,,"), ",")
I figured out the answer. I wasn't updating the cells I needed correctly. See code below:
count = 3
lastIndex = lastRow * 3
ReDim propertyArr(1, lastIndex)
For i = 1 To lastIndex Step 3
propertyArr(1, i) = originalWS.Cells((count - 1), 5)
count = count + 1
Next

Empty array at the end of the loop VBA Excel

I have the below code that adds values to an array if it meets a criteria.
It keeps looping horizontally through the columns across a row and then repeats the same for the next row and so on.
I am trying to clear the values accumulated in the array and empty it at the end of the columns loop:
For a = 21 To 23
Count = 0
For b = 2 To 36
If Not Worksheets("Sheet1").Cells(a, b).Value = "A" And Not Worksheets("Sheet1").Cells(a, b).Value = "B" Then
Count = Count + 1
Else
If Not Count = 0 Then
Dim arr() As Long
arrLen = 1
ReDim Preserve arr(1 To arrLen)
arr(arrLen) = Count
arrLen = arrLen + 1
For i = LBound(arr) To UBound(arr)
msg = msg & arr(i) & vbNewLine
Next i
Count = 0
End If
End If
Next b
MsgBox Worksheets("Sheet1").Cells(a, 1).Value & vbNewLine & msg
Erase arr 'not working
Next a
As you can see from the above code, I get a msgbox displaying the values at the end of each loop, however as it continues, the array keeps getting bigger and bigger indicating that the Erase line is not working.
Kindly help!
You can either use a loop to set the array elements to nulls, or ReDim the array away:
Sub marine()
Dim arr()
ReDim arr(1 To 2)
arr(1) = "Alpha"
arr(2) = "Beta"
ReDim arr(1 To 1)
arr(1) = ""
End Sub
That way you can re-use the same array name later in the sub.

Resources