Reputation: 107
Today I started studying Arrays in VBA.
After trying a few easy scripts I wanted to create one that is useful for my project.
In my excelsheet I have a datatable that needs to be transformed to new worksheets. Only for each column that has "Detail" in row 4.
The easiest way to imagine this would be by writing the values per relevant column to an array, reading and writing the results to a new sheet, and performing the action again.
But I think I'm using a wrong method to write the variables to my array. I looked through my code and all my declarions are not correct.
Could you help me out, how I can change the writing to the array correct?
Sub Import_data()
Dim LastCol As Integer
Dim LastRow As Long
Dim WS As Worksheet
Dim Arr() As Variant
Dim dim1 As Long, dim2 As Long
Set WS = Sheets("Budget to Table")
' Copy data from Budget to Table
With WS
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
dim1 = .Cells(.Rows.Count, "B").End(xlUp).Row - 5
dim2 = 4
' Copy information
For i = 3 To LastCol
If Cells(4, i).Value = "Detail" Then
ReDim Arr(0 To dim1, 0 To dim2)
For dim1 = LBound(Arr, 1) To UBound(Arr, 1)
For dim2 = LBound(Arr, 2) To UBound(Arr, 2)
Arr(dim1, 0) = Range(Cells(dim1, 2)) 'Should have the variable length but always column B
Arr(dim1, 1) = Range(Cells(dim1, i)) 'Should have the variable length but always column i
Arr(dim1, 2) = Range(Cells(1, i)) 'Is always the same header info from row 1 of the chosen column
Arr(dim1, 3) = Range(Cells(2, i)) 'Is always the same header info from row 2 of the chosen column
Arr(dim1, 4) = Range(Cells(3, i)) 'Is always the same header info from row 3 of the chosen column
Next dim2
Next dim1
End If
'writing the contents in a new sheet
Worksheet.Add
For dim1 = LBound(Arr, 1) To UBound(Arr, 1)
For dim2 = LBound(Arr, 2) To UBound(Arr, 2)
ActiveCell.Offset(dim1, dim2).Value = Arr(dim1, dim2)
Next dim2
Next dim1
Erase Arr
Next i
End With
End Sub
If I need to provide any more guidance please let me know.
I guess that the value of the dim1
and dim2
are never changing, so this doesn't create the loop i'm after.
edit: I uploaded the file here: https://dubblej15.stackstorage.com/s/C0DrKzFDxn4gY4U
I manually performed the action twice, what my result should look like. Maybe there is a better or easier way, but I thought that arrays could fit the job perfectly.
Thanks in advance!
Upvotes: 0
Views: 2889
Reputation: 4977
There are a few issues with your code (do watch out for those unqualified Ranges), but the main one is that you're getting your array indexes mixed up with the cell row and column references, and as you point out, there are a few pieces of redundant code where you dimension your array. Redim Preserve
is also limited when you use multi-dimensional arrays.
So, immediately below is a modified version of your code which shows the required adjustments.
However, if you want to work with arrays, then you can be much more efficient. For example, you can read ranges into arrays and write from arrays to ranges in just one line of code (which is way faster than using loops). The second piece of code shows you a more efficient way of handling the task - I wasn't sure if your sample rows will all have "Details' in column "A", because if they do without interruption, then the code could be even shorter.
Your modified code:
Dim dataWs As Worksheet, newWs As Worksheet
Dim lastRow As Long, lastCol As Long
Dim c As Long, r As Long, i As Long, j As Long
Dim arr() As Variant
'Read the data into an array
Set dataWs = ThisWorkbook.Worksheets("Budget to Table")
With dataWs
lastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
lastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
End With
'Loop through each of the data columns.
For c = 3 To lastCol
If Not IsEmpty(dataWs.Cells(3, c)) Then 'looks lik you only want the yellow columns.
'Dimension the array for number of rows
ReDim arr(1 To lastRow - 4, 1 To 5)
'Loop through each row in data array and transfer it.
With dataWs
For r = 5 To lastRow
arr(r - 4, 1) = .Cells(r, 2).Value
arr(r - 4, 2) = .Cells(r, c).Value
arr(r - 4, 3) = .Cells(1, c).Value
arr(r - 4, 4) = .Cells(2, c).Value
arr(r - 4, 5) = .Cells(3, c).Value
Next
End With
'Create a new sheet.
With ThisWorkbook.Worksheets
Set newWs = .Add(After:=.Item(.Count))
newWs.Name = arr(1, 5) 'name it for ease of use.
End With
'Write array onto the new sheet - the inefficient way
For i = 1 To UBound(arr, 1)
For j = 1 To UBound(arr, 2)
newWs.Cells(i, j).Value = arr(i, j)
Next
Next
End If
Next
A different way of handling arrays:
Dim ws As Worksheet
Dim data As Variant, output() As Variant
Dim rowList As Collection
Dim c As Long, i As Long
Dim r As Variant
'Read the data into an array
With ThisWorkbook.Worksheets("Budget to Table")
data = .Range(.Range("A1"), _
.Range(.Cells(1, .Columns.Count).End(xlToLeft), _
.Cells(.Rows.Count, "B").End(xlUp))) _
.Value2
End With
'Find the first dimension indexes with "Detail" in column A.
'We'll create a collection of our target row numbers.
Set rowList = New Collection
For i = 1 To UBound(data, 1)
If data(i, 1) = "Detail" Then rowList.Add i
Next
'Loop through each of the data columns.
For c = 3 To UBound(data, 2)
If Not IsEmpty(data(3, c)) Then 'looks lik you only want the yellow columns.
'Dimension the array for number of rows
ReDim output(1 To rowList.Count, 1 To 5)
i = 1 'row index for output array
'Loop through each row in data array and transfer it.
For Each r In rowList
output(i, 1) = data(r, 2)
output(i, 2) = data(r, c)
output(i, 3) = data(1, c)
output(i, 4) = data(2, c)
output(i, 5) = data(3, c)
i = i + 1
Next
'Create a new sheet.
With ThisWorkbook.Worksheets
Set ws = .Add(After:=.Item(.Count))
ws.Name = output(1, 5) 'name it for ease of use.
End With
'Write array onto the new sheet.
ws.Range("A1").Resize(UBound(output, 1), UBound(output, 2)).Value = output
End If
Next
Upvotes: 3
Reputation: 7567
Using a Dynamic variant array is more simple.
Sub Import_data()
Dim LastCol As Integer
Dim LastRow As Long
Dim WS As Worksheet
Dim Arr() As Variant, vDB As Variant
Dim i As Integer, j As Long, n As Long
Set WS = Sheets("Budget to Table")
' Copy data from Budget to Table
With WS
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
vDB = .Range("a1", .Cells(LastRow, LastCol)) '<~~ get data to vDB variant array from range
' Copy information
For i = 3 To LastCol
n = 0
If vDB(4, i) = "Detail" Then
For j = 5 To UBound(vDB, 1)
n = n + 1
ReDim Preserve Arr(1 To 5, 1 To n) '<~set dynamic variant array which is to be transposed.
Arr(1, n) = vDB(j, 2)
Arr(2, n) = vDB(j, i)
Arr(3, n) = vDB(1, i)
Arr(4, n) = vDB(2, i)
Arr(5, n) = vDB(3, i)
Next j
'writing the contents in a new sheet
Worksheets.Add after:=Sheets(Sheets.Count)
Range("a1").Resize(n, 5) = WorksheetFunction.Transpose(Arr)
ReDim Arr(1 To 5, 1 To 1)
End If
Next i
End With
End Sub
Upvotes: 3