Load a variable range into an array - excel

I want to store a range of variable size in an one-dimensional array. The range starts at A2 and goes to the last row of the same column. My approach looks like that. It's flawed.
Option Explicit
Sub Range_to_Array()
Dim i, j, k As Integer
Dim arr1(), arr2(), arr3(), arr4() As Variant
With Worksheets("table1")
arr1() = .Cells(.Range("A2"), .Range("A1").End(xlDown).Row)
End With
End Sub

Please, try the next way. Application.Transpose transforms a 2D array with a column in a 1D type. No iteration needed:
Sub Array1DFromColumnRange()
Dim ws As Worksheet, lastR As Long, arr
Set ws = Worksheets("table1")
lastR = ws.Range("A" & ws.rows.count).End(xlUp).Row
arr = Application.Transpose(ws.Range("A2:A" & lastR).Value)
Debug.Print Join(arr, "|") 'just to visually see the result in Immediate Window (Ctrl + G)...
End Sub
The returned 1D array is 1 based, as the 2D array directly extracted from a range. To transform it in zero based type, can be done without iteration, too:
'change the array type to be zero based:
ReDim Preserve arr(0 To UBound(arr) - 1)
Debug.Print LBound(arr)
Debug.Print Join(arr, "|")

Your problem is that your Range-Definition is wrong.
Cells expect 2 parameters (row and column) to address one cell. This is not what you want, and even if, your parameters would be wrong.
What you need in your case is Range.
Now Range can be called either with one or two parameters.
If you call it with one parameter, this defines the whole range.
Examples: Range("A1") or Range("B2:C5") or Range("B:B")
Whats often used in VBA is something like Range("A1:A" & lastRow)
If you call it with two parameters, those two parameters define the first and last cell of the range.
Examples: Range(Range("A1"), Range("C10")) or Range(Cells(1, 1), Cells(10, 3))
I would advice to define an intermediate variable to save the Range - makes it much easier to debug. Also the row number of the last cell should go into an intermediate variable.
In your case you could use one of the following
Dim r As Range, lastRow As Long
' Get the last row in use (usually its better to go up from the end of the sheet)
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
' 2 parameters
Set r = .Range(.Cells(2, 1), .Cells(2, lastRow))
' 1 Parameter, last row is concatenated to the range definition
Set r = .Range("A2:A" & lastRow)
' Use Resize
Set r = .Range("A2").Resize(lastRow-1, 1) ' -1 because you start at row 2
arr1 = r.Value

Try this instead.
Sub Range_to_Array()
Dim i As Integer, j As Integer, k As Integer
Dim arr1() As Variant, arr2() As Variant, arr3() As Variant, arr4() As Variant
Dim myRange As Range
With Worksheets("table1")
arr1 = .Range(.Range("A2"), .Range("A1").End(xlDown)).Value
End With
Debug.Print arr1(1, 1)
End Sub
Also please note that in order to properly declare variables, you need to specify data type for each variable separately.
Dim i, j, k As Integer
actually means
Dim i As Variant, j As Variant, k As Integer

Related

Alternative solution for looping through the Range in VBA

Recently I was wondering about the possibility of speeding up the program by switching a part of the code below:
Dim cell as Variant
Dim myValues() as Double
ReDim myValues(myRange.Count - 1)
Dim i as Integer: i = 0
For Each cell in myRange.Cells
myValue(i) = cell.Value
i = i + 1
Next cell
into a loop where I could refer to the value of each cell directly, instead of instantiating a cell object, assigning it a cell value from a given range, and then extracting the cell value.
In my mind, the pseudocode would look something like this:
Dim cellValue as Double
Dim myValues() as Double
ReDim myValues(myRange.Count - 1)
Dim i as Integer: i = 0
For each cellValue in myRange.Cells.Values
myValues(i) = cellValue
i = i + 1
Next cellValue
If my overall concept is wrong from the start, or if you can't get the cells values from Range faster, then there was no question.
Due to the fact that this is my first post, I could have written something incorrectly / in wrong posting convention. Let me know and I'll fix it.
Cheers
As #SJR notes, this is a common way to access data from a range without going cell-by-cell
Dim arr, i as long, rng As Range
Set rng = Activesheet.Range("A1:B20")
arr = rng.Value 'arr is now a 2D array (1 To 20, 1 To 2)
'Note: base is 1 and not the more-typical 0
For i = 1 to ubound(arr,1)
Debug.Print arr(i, 1), arr(i, 2)
Next i
arr(3, 2) = 999
rng.value = arr 'put the data back to the range
If the reason is for getting rid of the 2D array a function like this can be the solution:
Function VectorFromRange(rng As Range) As Variant
Dim arrIn As Variant
arr = rng.value 'Dumping the data from range
Dim i As Long
Dim item As Variant
ReDim arrOut(1 To rng.Cells.Count) As Variant
For Each item In arr
i = i + 1
arrOut(i) = item
Next item
VectorFromRange = arrOut
End Function

Split String to multiple cells

I have a problem.
I have this:
("boufous;othman;212544;casa")
I want to split this cell to multiple cells like:
cell[1]=boufous cell[2]=othman cell[3]=212544
cell[4]=casa
For Each olMailItem In olItems
//Code here
i = i + 1
Next olMailItem
There really are two ways that I personally like working through if you must use VBA. Imagine the following data:
1. Split
As per the comments given, you can utilize a function called Split. Hereby a small script which shows how you could approach this involving a small loop:
Sub UseSplit()
Dim arr As Variant
Dim lr As Long, x As Long
With Sheet1 'Change CodeName accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
arr = .Range("A1:A" & lr)
For x = LBound(arr) To UBound(arr)
.Cells(x, 2).Resize(1, UBound(Split(arr(x, 1), ";")) + 1) = Split(arr(x, 1), ";")
Next x
End With
End Sub
2. TextToColumns
A second approach would be to utilize the build-in function to write delimited text to other columns using the TextToColumns function. This would not involve a loop. Underneath a small example:
Sub UseSplit()
Dim rng As Range
Dim lr As Long
With Sheet1 'Change CodeName accordingly
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
Set rng = .Range("A1:A" & lr)
rng.TextToColumns Destination:=.Range("B1"), Semicolon:=True
End With
End Sub
The advantage with this is that, whereas Split will return an array of string values, text to columns will not:
Values that are meant to be numeric, are actually numeric.
The question remains, do you really need to work through VBA? Either way, good luck with your project =)
Assuming olMailItem as a string and below is the code
Dim str1, str2
olMailItem = "boufous;othman;212544;casa"
str1 = Split(olMailItem, ";")
str2 = UBound(str1)
For i = 0 To str2
Debug.Print str1(i)
Next i

How to copy only visible/filtered array values to clipboard?

So I have a worksheet of data and I want to copy a comma-delimited array to my clipboard. If I have to paste the value into a cell first, that is fine as well. The worksheet has autofilters on and is filtered. I only want to select the values that are currently visible due to the filtering, not the whole array.
The array is in column P and starts in P2. I have a LastRow set up, and have been able to get the comma-delimited part to work, but am having trouble with the copying to clipboard part and the visible values only part.
The code below creates the comma-delimited list and I can show it in a message box or something, but I'm not sure how to copy it to the clipboard or how to make sure only visible values are being selected.
Dim LastRow As Long
LastRow = Range("P" & Rows.Count).End(xlUp).Row
Dim arr
arr = Join(Application.Transpose(Range("P2:P" & LastRow).Value), ",")
Try this code
Sub Test()
Dim arr, rng As Range, c As Range, n As Long
Set rng = Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)
ReDim a(1 To rng.Cells.Count)
For Each c In rng
n = n + 1: a(n) = c.Value
Next c
arr = Join(a, ",")
End Sub
Range("P2:P" & Cells(Rows.Count, "P").End(xlUp).Row).SpecialCells(xlCellTypeVisible)

Fill dynamic array with the rows of visible cells

I am getting an error of "Subscript out of Range" when I'm trying to add the row value, of visible cells (minus the header) to the array. Below the code:
Dim Rng As Range
Dim r As Range
Dim i as Long
Dim arr() As Long
Set Rng = ActiveSheet.UsedRange.Resize(ActiveSheet.UsedRange.Rows.Count - 1, ActiveSheet.UsedRange.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible)
i = 0
For Each r In Rng.Rows
'Debug.Print r.Row
arr(i) = r.Row
i = i + 1
Next
Am I forgetting something ?! I'm still new to VBA and more so, to arrays.
This function works fine...
I didn't get how you are calculating the range... but this output is coming out of the range only.. array is totally dynamic
Sub foo()
Dim Rng As Range
Dim r As Range
Dim i As Long
Dim arr() As Variant
Set Rng = ActiveSheet.UsedRange.Resize(ActiveSheet.UsedRange.Rows.Count - 1, ActiveSheet.UsedRange.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible)
i = 1
For Each r In Rng.Rows
ReDim Preserve arr(i)
arr(i) = r.Row
Debug.Print arr(i)
i = i + 1
Next
End Sub
As already pointed out, you have to define the array. You can give it a fixed size when defining it (Dim arr(2) as Integer).
Dynamic ranges can be made with using ReDim. With Preserve it saves the values in the array when redefining the size. (Note: You can only ReDim the last dimension of an array)
The problem is that although you are declaring the array, you haven't initialised it with a size, so there are no elements in the array hence the subscript out of range.
Your code should read, note the other problem you will have is how you are trying to address the range, I have corrected below:
Dim Rng As Range, r As Range
Dim i as integer
Dim ary() as Long
Set Rng = ActiveSheet.UsedRange.Resize(ActiveSheet.UsedRange.Rows.Count - 1, ActiveSheet.UsedRange.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible)
Redim ary(Rng.Rows.Count)
i = 0
For Each r In Rng.Rows
'Debug.Print r.Row
arr(i) = CLng(r.Row)
i = i + 1
Next
This is a tested and working example

It is possible to have an array with no upper bound?

I'm populating an array based on whether or not criteria across a worksheet are met. The thing is, I don't know how long the array is, as it could be larger than the dataset itself (will have duplicates of some entries). What I have is something along the lines of:
Sub ArrayTest
Dim MyArray as Variant
Dim i as Long
Dim index as Long
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).row
ReDim myarray(index)
For i = 1 to lastrow
If (something) then
index = index + 1
ReDim Preserve recordArrray(Index)
recordArrray(Index) = range("A" & i)
End If
Next
End Sub
However, once index reaches lastrow, I get a subscript out of range error. I can avoid this by simply replacing the ReDim line with ReDim myarray(1 to 100000000). Obviously, this is not an ideal solution.
Can I create an array without having to define what the upper bound will be before it is created?
Arrays always have to have both an upper and lower bound (though you can change the boundaries dynamically with Redim).
My guess is that the problem you're running into is that the starting value for index is 0 which is outside of the legal bounds of your array. I suspect that this would fix the immediate problem:
Sub ArrayTest
Dim MyArray as Variant
Dim i as Long
dim index as Long
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).row
ReDim myarray(1 to lastrow)
For i = 1 to lastrow
If (something) then
index = index + 1
recordArrray(Index) = range("A" & i)
End If
Next
End Sub

Resources