I cannot get to work condition for matching 2D arrays. I have tried another approach and this one is closer to the solution, but still does not produce the outcome.
This is what I want to do:
In sheet1 I have different dates that go through columns and size is uncertain. Below these dates are the values:
In sheet 2, I have a smaller subset of dates (that should exist in sheet1):
Through the code, I want to match the dates in sheet1 and sheet2, and only if match is true, I want to write the corresponding values from sheet1 to sheet2.
This is the outcome:
I want to use Arrays for dates in sheet1 and sheet2 and if they match, write the array of values. But the arrays of dates turn to be empty and so condtion for match does not work. I am not getting any error message as well:
Sub test()
Dim arrAmounts() As Variant
Dim arrDates_w2() As Variant
Dim arrDates_w1() As Variant
Dim Lastcol_w2 As Integer
Dim Lastcol_w1 As Integer
Dim LastRow As Integer
Dim i As Integer
Dim w As Integer
Dim d As Integer
Dim f As Integer
Dim g As Integer
Dim w1 As Worksheet
Dim w2 As Worksheet
Set w1 = Sheets("Sheet1")
Set w2 = Sheets("Sheet2")
LastRow = 17 'last row on both sheets
f = 1
g = 1
With w2
Lastcol_w2 = .Cells(3, Columns.Count).End(xlToLeft).Column
'array of dates in w2
ReDim arrDates_w2(1, Lastcol_w2)
End With
With w1
Lastcol_w1 = .Cells(3, Columns.Count).End(xlToLeft).Column
'Assign arrays:
ReDim arrAmounts(LastRow, Lastcol_w1)
ReDim arrDates_w1(1, Lastcol_w1)
For i = 1 To LastRow
For d = 1 To UBound(arrDates_w1, 2)
arrAmounts(i, d) = .Cells(3 + i, 2 + d)
Next
Next
'Match the dates in worksheets 1 and 2
For i = 1 To LastRow
For w = 1 To UBound(arrDates_w2, 2)
For d = 1 To UBound(arrDates_w1, 2)
If arrDates_w2(1, w) = arrDates_w1(1, d) Then
w2.Cells(i + 3, 2 + w) = arrAmounts(i, f + 3)
End If
Next
Next
Next
End With
End Sub
I would appreciate suggestions.
Please try this code.
Option Explicit
Sub CopyColumns()
Const CaptionRow As Long = 3 ' on all sheets
Const FirstClm As Long = 3 ' on all sheets
Dim WsIn As Worksheet ' Input sheet
Dim WsOut As Worksheet ' Output sheet
Dim DateRange As Range ' dates on WsIn
Dim Cin As Long ' input column
Dim Rl As Long ' last row in WsIn
Dim Cl As Long ' last used column in WsOut
Dim C As Long ' column counter in WsOut
Dim Arr As Variant ' transfer values
Set WsIn = Worksheets("Sheet1")
Set WsOut = Worksheets("Sheet2")
With WsIn
Set DateRange = .Range(.Cells(CaptionRow, FirstClm), .Cells(CaptionRow, .Columns.Count).End(xlToLeft))
End With
With WsOut
Cl = .Cells(CaptionRow, .Columns.Count).End(xlToLeft).Column
For C = FirstClm To Cl
On Error Resume Next
Cin = Application.Match(.Cells(CaptionRow, C).Value2, DateRange, 0)
If Err = 0 Then
Cin = Cin + DateRange.Column - 1
Rl = WsIn.Cells(WsIn.Rows.Count, Cin).End(xlUp).Row
Arr = WsIn.Range(WsIn.Cells(CaptionRow + 1, Cin), WsIn.Cells(Rl, Cin)).Value
.Cells(CaptionRow + 1, C).Resize(UBound(Arr)).Value = Arr
End If
Next C
End With
End Sub
What do you expect ReDim arrDates_w2(1, Lastcol_w2) to be doing? As it stands, it's only re-sizing the number of items that can be held in the array... You need to assign the Range to it: arrDates_w2 = w2.Range("C3:K3").Value for example. This will create a multi-dimensional array.
Then you can loop the items. Here's some sample code to illustrate the principle
Sub GetArrayInfo()
Dim a As Variant, i As Long, j As Long
Dim w2 As Worksheet
Set w2 = Sheets("Sheet2")
a = ws.Range("C3:K3").Value2
Debug.Print UBound(a, 1), UBound(a, 2)
For j = 1 To UBound(a, 2)
For i = 1 To UBound(a, 1)
Debug.Print a(i, j)
Next
Next
End Sub
Try
Sub test()
Dim Ws As Worksheet, Ws2 As Worksheet
Dim c As Integer, j As Integer, p As Integer
Dim i As Long, r As Long
Dim arr1() As Variant, arr2() As Variant
Dim rngDB As Range, rngHead As Range
Set Ws = Sheets("Sheet1")
Set Ws2 = Sheets("Sheet2")
With Ws
c = .Cells(3, Columns.Count).End(xlToLeft).Column
r = .Range("c" & Rows.Count).End(xlUp).Row
Set rngHead = .Range("c3", .Cells(3, c))
arr1 = .Range("c3", .Cells(r, c))
End With
With Ws2
c = .Cells(3, Columns.Count).End(xlToLeft).Column
Set rngDB = .Range("c3", .Cells(r, c))
arr2 = rngDB
End With
For j = 1 To UBound(arr2, 2)
p = WorksheetFunction.Match(arr2(1, j), rngHead, 0)
For i = 2 To UBound(arr2, 1)
arr2(i, j) = arr1(i, p)
Next i
Next j
rngDB = arr2
End Sub
Related
There are some questions about splitting arrays but in my case something goes wrong when I want to output the arrays onto the Worksheet. Also, my solution seems a bit complicated.
My goal is to split a 1-dimensional array with 2.5 mln elements in 4 parts to be able to easily output it to a Worksheet (625,000 rows, 4 columns).
SomeSub() is where the data originates, in this case "i Mod 99" generates some "random" numbers to see some output on the Worksheet. SomeSub() calls the sub SplitArray() which is very the splitting happens. I only have data in a 1-dimensional array but I thought I need to use a 2-dimensional one so that I can get the values from columns into rows by transposing them. Not sure this is actually needed but it works to some degree.
Sub SomeSub()
Dim i As Long
Dim bigarr(1, 2500000) As Integer
Dim timing As Single
timing = Timer
For i = 1 To 2500000
bigarr(1, i) = i Mod 99
Next i
Call SplitArray(bigarr)
Debug.Print Format(Timer - timing, "0.0") & " seconds"
End Sub
Sub SplitArray(ByRef arr0() As Integer)
Dim i As Long
Dim arr1(1, 625000) As Integer
Dim arr2(1, 625000) As Integer
Dim arr3(1, 625000) As Integer
Dim arr4(1, 625000) As Integer
For i = 1 To 625000: arr1(1, i) = arr0(1, i): Next i
For i = 625001 To 1250000: arr2(1, i - 625000) = arr0(1, i): Next i
For i = 1250001 To 1875000: arr3(1, i - 1250000) = arr0(1, i): Next i
For i = 1875001 To 2500000: arr4(1, i - 1875000) = arr0(1, i): Next i
Dim vektor As Variant
Worksheets("Output").Select
vektor = Application.WorksheetFunction.Transpose(arr1)
Range(Cells(11, 1), Cells(625010, 1)).Value = vektor
vektor = Application.WorksheetFunction.Transpose(arr2)
Range(Cells(11, 2), Cells(625010, 2)).Value = vektor
vektor = Application.WorksheetFunction.Transpose(arr3)
Range(Cells(11, 3), Cells(625010, 3)).Value = vektor
vektor = Application.WorksheetFunction.Transpose(arr4)
Range(Cells(11, 4), Cells(625010, 4)).Value = vektor
End Sub
The problem is that my approach works only until row 35186 but not until row 625,010.
Currently the whole procedure takes about 1.9 seconds using 1 thread. This is usually fast enough but a quicker or simpler solution to splitting a "long array" would also be appreciated.
Change your arrays to be vertical instead of horizontal and you can avoid Application.Transpose which has a limit to the number of items it allows:
Sub SplitArray(ByRef arr0() As Integer)
Dim i As Long
Dim arr1(625000, 1) As Integer
Dim arr2(625000, 1) As Integer
Dim arr3(625000, 1) As Integer
Dim arr4(625000, 1) As Integer
For i = 1 To 625000: arr1(i, 1) = arr0(1, i): Next i
For i = 625001 To 1250000: arr2(i - 625000, 1) = arr0(1, i): Next i
For i = 1250001 To 1875000: arr3(i - 1250000, 1) = arr0(1, i): Next i
For i = 1875001 To 2500000: arr4(i - 1875000, 1) = arr0(1, i): Next i
With Worksheets("Output")
.Range(.Cells(11, 1), .Cells(625010, 1)).Value = arr1
.Range(.Cells(11, 2), .Cells(625010, 2)).Value = arr2
.Range(.Cells(11, 3), .Cells(625010, 3)).Value = arr3
.Range(.Cells(11, 4), .Cells(625010, 4)).Value = arr4
End With
End Sub
But you really only need one output array with 4 columns:
Sub SplitArray(ByRef arr0() As Integer)
Dim i As Long
Dim arr1(625000, 4) As Integer
For i = 1 To 625000: arr1(i, 1) = arr0(1, i): Next i
For i = 625001 To 1250000: arr1(i - 625000, 2) = arr0(1, i): Next i
For i = 1250001 To 1875000: arr1(i - 1250000, 3) = arr0(1, i): Next i
For i = 1875001 To 2500000: arr1(i - 1875000, 4) = arr0(1, i): Next i
With Worksheets("Output")
.Range(.Cells(11, 1), .Cells(625010, 4)).Value = arr1
End With
End Sub
Split a 1D Array
Simple
The GetSplitOneD function will return the columns in a 2D one-based array whose values can easily be written (copied) to a range e.g.:
rg.Resize(UBound(Data, 1), UBound(Data, 2)).Value = Data
Sub GetSplitOneDtest()
Dim Timing As Double: Timing = Timer
Const nCount As Long = 2500000
Const ColumnsCount As Long = 4
Const dName As String = "Output"
Const dFirstCellAddress As String = "A2"
' Reference the workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim n As Long
' Create the source (sample) array i.e. return the numbers
' from 1 to 'nCount' in a 1D one-based array.
Dim sArr() As Variant: ReDim sArr(1 To nCount)
For n = 1 To nCount
sArr(n) = n
Next n
' Using the 'GetSplitOneD' function, return the split values
' from the source array in the destination array ('dData'),
' a 2D one-based array.
Dim dData() As Variant: dData = GetSplitOneD(sArr, ColumnsCount)
' Write the destination rows count to a variable ('drCount').
Dim drCount As Long: drCount = UBound(dData, 1)
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' Reference the destination first cell ('dfCell').
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
' Calculate the destination clear rows count ('dcrCount'),
' the number of rows to be cleared below the destination ranges.
Dim dcrCount As Long: dcrCount = dws.Rows.Count - dfCell.Row - drCount + 1
' Write the values from the destination array to the destination ranges.
With dfCell.Resize(, ColumnsCount) ' reference the first row
.Resize(drCount).Value = dData ' write
.Resize(dcrCount).Offset(drCount).Clear ' clear below
End With
Debug.Print Format(Timer - Timing, "0.000") & " seconds"
' Inform.
MsgBox "Data split.", vbInformation
End Sub
Function GetSplitOneD( _
SourceOneD() As Variant, _
ByVal ColumnsCount As Long) _
As Variant()
Dim sCount As Long: sCount = UBound(SourceOneD) - LBound(SourceOneD) + 1
Dim drCounts() As Long: ReDim drCounts(1 To ColumnsCount)
Dim drCount As Long: drCount = Int(sCount / ColumnsCount)
Dim Remainder As Long: Remainder = sCount Mod ColumnsCount
If Remainder > 0 Then
drCount = drCount + 1
drCounts(ColumnsCount) = drCount - ColumnsCount + Remainder
Else
drCounts(ColumnsCount) = drCount
End If
Dim c As Long
For c = 1 To ColumnsCount - 1: drCounts(c) = drCount: Next c
Dim dData() As Variant: ReDim dData(1 To drCount, 1 To ColumnsCount)
Dim s As Long: s = LBound(SourceOneD)
Dim dr As Long
For c = 1 To ColumnsCount
For dr = 1 To drCounts(c)
dData(dr, c) = SourceOneD(s)
s = s + 1
Next dr
Next c
GetSplitOneD = dData
End Function
More Flexible
The GetJaggedSplitOneD function will return the columns in a jagged array containing as many 2D one-based one-column arrays as there are columns. Then you could write each column to another place instead of writing them next to each other. In the test sub, you could change the value of the dcGap constant determining how many empty columns should be in-between. If you don't need this additional functionality, use the first function since it's a little bit faster.
Sub GetJaggedSplitOneDtest()
Dim Timing As Double: Timing = Timer
' Define constants.
Const nCount As Long = 2500000
Const ColumnsCount As Long = 4
Const dName As String = "Output"
Const dFirstCellAddress As String = "A2"
Const dcGap As Long = 2 ' empty columns in-between
' Reference the workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim n As Long
' Create the source (sample) array i.e. return the numbers
' from 1 to 'nCount' in a 1D one-based array.
Dim sArr() As Variant: ReDim sArr(1 To nCount)
For n = 1 To nCount
sArr(n) = n
Next n
' Using the 'GetJaggedSplitOneD' function, return the split values
' from the source array in the destination array ('dJAG'), a jagged array
' containing 4 ('ColumnsCount') same-sized 2D one-based one-column arrays.
Dim dJag() As Variant: dJag = GetJaggedSplitOneD(sArr, ColumnsCount)
' Write the destination rows count to a variable ('drCount').
Dim drCount As Long: drCount = UBound(dJag(1), 1)
' Reference the destination worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' Reference the destination first cell ('dfCell').
Dim dfCell As Range: Set dfCell = dws.Range(dFirstCellAddress)
' Calculate the destination clear rows count ('dcrCount'),
' the number of rows to be cleared below the destination ranges.
Dim dcrCount As Long: dcrCount = dws.Rows.Count - dfCell.Row - drCount + 1
' Write the values from the destination array to the destination ranges.
For n = 1 To ColumnsCount
With dfCell
.Resize(drCount).Value = dJag(n) ' write
.Resize(dcrCount).Offset(drCount).Clear ' clear below
End With
Set dfCell = dfCell.Offset(, dcGap + 1)
Next n
Debug.Print Format(Timer - Timing, "0.000") & " seconds"
' Inform.
MsgBox "Data split.", vbInformation
End Sub
Function GetJaggedSplitOneD( _
SourceOneD() As Variant, _
ByVal ColumnsCount As Long) _
As Variant()
Dim sCount As Long: sCount = UBound(SourceOneD) - LBound(SourceOneD) + 1
Dim drCounts() As Long: ReDim drCounts(1 To ColumnsCount)
Dim drCount As Long: drCount = Int(sCount / ColumnsCount)
Dim Remainder As Long: Remainder = sCount Mod ColumnsCount
If Remainder > 0 Then
drCount = drCount + 1
drCounts(ColumnsCount) = drCount - ColumnsCount + Remainder
Else
drCounts(ColumnsCount) = drCount
End If
Dim c As Long
For c = 1 To ColumnsCount - 1: drCounts(c) = drCount: Next c
Dim dJag() As Variant: ReDim dJag(1 To ColumnsCount)
Dim dData() As Variant: ReDim dData(1 To drCount, 1 To 1)
Dim s As Long: s = LBound(SourceOneD)
Dim dr As Long
For c = 1 To ColumnsCount
dJag(c) = dData
For dr = 1 To drCounts(c)
dJag(c)(dr, 1) = SourceOneD(s)
s = s + 1
Next dr
Next c
GetJaggedSplitOneD = dJag
End Function
I'm trying to create a loop on the below code so if there are multiple matches of Column A to Column B it continue to fill out column B with the data from column A.
I've been suggested to create variant arrays and loop arrays, but I'm not that advanced yet after looking into it. Thanks.
Sub Test_match_fill_data()
Dim aCell
Dim e, k As Long, matchrow As Long
Dim w1, w2 As Worksheet
Dim cell As Range
Set w1 = Workbooks("Book1").Sheets("Sheet1")
Set w2 = Workbooks("Book2").Sheets("Sheet2")
e = w1.Cells(w1.Rows.Count, 1).End(xlUp).Row
k = w2.Cells(w2.Rows.Count, 1).End(xlUp).Row
For Each aCell In w1.Range("A2:A" & e)
On Error Resume Next
matchrow = w2.Columns("A:A").Find(What:=Left$(aCell.Value, 6) & "*", LookAt:=xlWhole).Row
On Error GoTo 0
If matchrow = 0 Then
Else
w2.Range("B" & matchrow).Value = aCell.Offset(0, 1).Value
End If
matchrow = 0
Next
End Sub
Your code would work if you searched Book1 for values from Book2. Here is an array version.
Option Explicit
Sub Test_match_fill_data()
Dim w1 As Worksheet, w2 As Worksheet
Dim ar1, ar2, matchrow, n As Long
Dim lastRow As Long, i As Long, s As String
Set w1 = Workbooks("Book1").Sheets("Sheet1")
With w1
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
ar1 = .Range("A2:B" & lastRow).Value2
End With
Set w2 = Workbooks("Book2").Sheets("Sheet2")
With w2
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
ar2 = .Range("A2:B" & lastRow).Value2
End With
For i = 1 To UBound(ar2)
s = Left(ar2(i, 1), 6)
If Len(s) > 0 Then
matchrow = Application.Match(s & "*", Application.Index(ar1, 0, 1), 0)
'Debug.Print i, s, matchrow
If Not IsError(matchrow) Then
ar2(i, 2) = ar1(matchrow, 2)
n = n + 1
End If
End If
Next
' copy array back to sheet
w2.Range("A2:B" & UBound(ar2) + 1) = ar2
MsgBox n & " rows updated"
End Sub
You can use the INDEX/MATCH formula - and then replace the results by values - no need for an array etc.
I put my assumptions in the code
Sub insertConsultants()
Dim wb1 As Workbook
Set wb1 = Workbooks("wb1.xlsx")
Dim rgDataSource As Range
'Assumption: Make = column A - first value in A3
'maybe you have to adjust this to your needs
'CurrentRegion: no empty rows within in data area
Set rgDataSource = wb1.Worksheets(1).Range("A3").CurrentRegion
Dim wb2 As Workbook: Set wb2 = Workbooks("wb2.xlsx")
Dim rgTarget As Range
'Assumption: Make = column A - first value in A3
'maybe you have to adjust this to your needs
Set rgTarget = wb2.Sheets(1).Range("A3").CurrentRegion
With rgTarget .Offset(, 1).Resize(, 1)
' = consultants column
.Formula = "=INDEX(" & rgDataSource.Columns(2).Address(True, True, , True) & ",MATCH(A3," & rgDataSource.Columns(1).Address(True, True, , True) & ",0))"
.Value = .Value
End With
End Sub
IMPORTANT: you always have to define each variable indivdually:
With your code Dim w1, w2 As Worksheet w1 is a variant not a worksheet. This could lead to errors.
This is a follow up question to my previous VBA question. Someone provided me with a potential solution for a lag in performance, and mentioned instead of looping through the actual cells in each column, transform the columns into Arrays and then load the results into a new Array.
I keep getting "subscript out of range" issues, among other various errors. I've manipulated these Arrays so many times with ReDim and others to try to load the results, but I keep hitting the same issue. You will see some of the code I tried where things are commented out.
How can I properly load these results based on the information I have? I thought at first it was because I was declaring a dynamic, empty Array, so that's why I used the UBound of an array of the same size in a ReDim.
Sub Missing_CAT():
Dim i As Variant
Dim j As Variant
'Dim j As Long
'Dim h As Long
'Dim h As Variant
Dim d As Date
Dim e As Date
Dim f As Date
Dim a As String
Dim ws As Worksheet
Dim rowCount As Long
Dim secondRowCount As Long
Dim oDateArr() As Variant
Dim fromDateArr() As Variant
Dim toDateArr() As Variant
Dim perilArr() As Variant
Dim resultArr() As Variant
Dim cell As Variant
Dim counter As Variant
Dim count As Long
Dim boundary As Long
Dim ub As Integer
rowCount = Worksheets("raw_data_YOA").Cells(Rows.count, "A").End(xlUp).row
oDateArr = Sheets("raw_data_YOA").Range("Q2:Q" & rowCount).Value
ub = UBound(oDateArr)
ReDim resultArr(ub)
count = 0
'For i = 2 To rowCount
For Each i In oDateArr
'd = Worksheets("raw_data_YOA").Cells(i, 17).Value
d = i
For Each ws In Sheets
If ws.Name = "2020" Or ws.Name = "2019" Then
secondRowCount = ws.Cells(Rows.count, "D").End(xlUp).row
fromDateArr = ws.Range("D5:D" & secondRowCount).Value
toDateArr = ws.Range("E5:E" & secondRowCount).Value
perilArr = ws.Range("F5:F" & secondRowCount).Value
' For j = 5 to secondRowCount
'For Each j In fromDateArr
'boundary = UBound(fromDateArr)
For j = 1 To UBound(fromDateArr)
' MsgBox (fromDateArr(j))
e = fromDateArr(j, 1)
f = toDateArr(j, 1)
p = perilArr(j, 1)
'e = ws.Cells(j, 4).Value
' f = ws.Cells(j, 5).Value
If d >= e And d <= f Then
' ReDim Preserve resultArr(1 To UBound(resultArr) + 1)
' resultArr(UBound(resultArr), 1) = p
resultArr(count) = p
Exit For
ElseIf j = UBound(fromDateArr) Then
' Worksheets("raw_data_YOA").Cells(i, 63).Value = "FALSE"
' ReDim Preserve resultArr(1 To UBound(resultArr) + 1)
' MsgBox (UBound(resultArr))
resultArr(count) = "FALSE"
End If
Next j
Else
GoTo NextIteration
End If
count = count + 1
NextIteration:
Next
Next i
counter = 0
For Each cell In Sheets("raw_data_YOA").Range("Q2:Q" & rowCount)
cell.Value = resultArr(counter)
counter = counter + 1
Next
MsgBox ("Done")
End Sub
EDIT:
Specifically, the lines throwing the errors are resultArr(count) = ...
I want to run the code which has assigned some arrays in 2 worksheets and based on match returns the data from one sheet to the other.
In the sheet1:
Here, I have 3 columns of data (for an example there is not so many rows, but it will be many more):
In the sheet 2 I have the following data:
Therefore, what I want to do is to match the column C in sheet2 with column A in sheet 1, i.e. match based on the IDs. Based on this match, and If in sheet2 in column E is the text 'Yes', then from sheet1 column A and column B, I want to write the values into the sheet2, respectively in columns F and G. I.e. 'Name' and 'Number'.
Therefore this is my desired outcome to achieve in sheet2 after running the code:
I wanted to assign the columns of data from sheet1 to arrays and the same for column with IDs in sheet2. I will have much more data!
The problem with my code is that instead of returing the values from sheet1 one after another based on match with iID in sheet2, it actually returns only the first values for 'Name' and 'Number' from sheet1 for as many 'Yes' as it is in sheet2 (It doubles the returned values) instead of returning one after the other.
This is my code:
Sub test()
Dim w_result As Worksheet
Dim w1 As Worksheet
Dim r As Long
Dim d As Long
Dim intLastRow As Long
Dim IntLastRow_Result As Long
Dim IntLastCol As Long
Dim arrID() As Variant
Dim arrName() As Variant
Dim arrNumber() As Variant
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_result= .Sheets("Sheet2")
End With
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).row
IntLastRow_Result = w_result.Cells(Rows.Count, 3).End(xlUp).row
arrID = .Range(.Cells(5, 3), .Cells(intLastRow, 3))
arrName= .Range(.Cells(5, 1), .Cells(intLastRow, 1))
arrNumber= .Range(.Cells(5, 2), .Cells(intLastRow, 2))
For r = 1 To UBound(arrID , 1)
If Len(arrID (r, 1)) > 0 Then
For d = 4 To IntLastRow_Result
If w_result.Cells(d, 3) = arrID (r, 1) Then
If w_result.Cells(d, 5) = "Yes" Then
w_result.Cells(d, 6) = arrName(r, 1)
w_result.Cells(d, 7) = arrNumber(r, 1)
End If
End If
Next
End If
Next r
End With
End Sub
I will be very much appreciated for any help on that!
Your error is that each time a match is found, the For d =... loop overwrites previous results.
A quick and dirty fix is to test the result row for empty, if found to be empty write result, then exit the inner for loop.
Sub test()
Dim w_result As Worksheet
Dim w1 As Worksheet
Dim r As Long
Dim d As Long
Dim intLastRow As Long
Dim IntLastRow_Result As Long
Dim IntLastCol As Long
Dim arrID() As Variant
Dim arrName() As Variant
Dim arrNumber() As Variant
Dim ResultRow As Long
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_result = .Sheets("Sheet2")
End With
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
IntLastRow_Result = w_result.Cells(w_result.Rows.Count, 3).End(xlUp).Row '<~~ removed implicit active sheet reference
arrID = .Range(.Cells(5, 3), .Cells(intLastRow, 3))
arrName = .Range(.Cells(5, 1), .Cells(intLastRow, 1))
arrNumber = .Range(.Cells(5, 2), .Cells(intLastRow, 2))
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
For d = 4 To IntLastRow_Result
If w_result.Cells(d, 3) = arrID(r, 1) Then
If w_result.Cells(d, 5) = "Yes" Then
If IsEmpty(w_result.Cells(d, 6)) Then '<~~~ Added
w_result.Cells(d, 6) = arrName(r, 1)
w_result.Cells(d, 7) = arrNumber(r, 1)
Exit For '<~~~ Added
End If
End If
End If
Next
End If
Next r
End With
End Sub
Note: this is a very inefficient solution, but will do ok for small data sets.
Here's a more efficient version, utilising a Variant Array for the results, and updating the start index of the inner loop
Sub test()
Dim w_result As Worksheet
Dim w1 As Worksheet
Dim r As Long
Dim d As Long
Dim intLastRow As Long
Dim IntLastRow_Result As Long
Dim IntLastCol As Long
Dim arrID() As Variant
Dim arrName() As Variant
Dim arrNumber() As Variant
Dim Results() As Variant
Dim ResultStart As Long
Dim ResultRow As Long
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_result = .Sheets("Sheet2")
End With
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
IntLastRow_Result = w_result.Cells(w_result.Rows.Count, 3).End(xlUp).Row '<~~ removed implicit active sheet reference
Results = w_result.Cells(1, 1).Resize(IntLastRow_Result, 8).Value
w_result.Activate
arrID = .Range(.Cells(5, 3), .Cells(intLastRow, 3))
arrName = .Range(.Cells(5, 1), .Cells(intLastRow, 1))
arrNumber = .Range(.Cells(5, 2), .Cells(intLastRow, 2))
ResultStart = 4
For r = 1 To UBound(arrID, 1)
If Len(arrID(r, 1)) > 0 Then
For d = ResultStart To IntLastRow_Result
If Results(d, 3) = arrID(r, 1) Then
If Results(d, 5) = "Yes" Then
If IsEmpty(Results(d, 6)) Then '<~~~ Added
Results(d, 6) = arrName(r, 1)
Results(d, 7) = arrNumber(r, 1)
Exit For '<~~~ Added
End If
End If
ResultStart = ResultStart + 1
End If
Next
End If
Next r
End With
w_result.Cells(1, 1).Resize(IntLastRow_Result, 8).Value = Results
End Sub
The way I would solve approach this is the following. First, if you are working with a large dataset in Excel you do not want to loop through the front end range, but rather, loop through arrays (memory).
Now, how do we use arrays effectively? Well, what do we need? We need an array for the Sheet1 data, we need and array for the Sheet2 output data. Store your sheet1 data to an array AND store your sheet2 “template” data to an array for mapping purposes.
Sample data:
See below code. You will note that this approach will speed up your process time massively!
Option Explicit
Sub TransferUsingArrays()
Dim wsS1 As Worksheet, wsS2 As Worksheet
Dim aSheet1() As Variant, aSheet2() As Variant
Dim lRowS1 As Long, lRowS2 As Long
Dim i As Long, j As Long
'set the worksheets - use workbook qualifier!
Set wsS1 = ThisWorkbook.Sheets("Sheet1")
Set wsS2 = ThisWorkbook.Sheets("Sheet2")
lRowS1 = wsS1.Range("A" & wsS1.Rows.Count).End(xlUp).Row
lRowS2 = wsS2.Range("C" & wsS2.Rows.Count).End(xlUp).Row
'set the arrays
aSheet1 = wsS1.Range("A4").Resize(lRowS1, 3)
aSheet2 = wsS2.Range("C3").Resize(lRowS2, 5)
'now loop through the data array and match with sheet2 array entry
For i = 2 To lRowS1
'if there is a name, only look for match
If Len(aSheet1(i, 1)) > 0 Then
'now loop through second array to insert latest value
For j = 2 To lRowS2
'if the id is a match
If aSheet2(j, 1) = aSheet1(i, 3) Then
'if there is a blank in name and there is yes in value
If aSheet2(j, 5) = "" And aSheet2(j, 3) = "Yes" Then
'now insert the values into second array
aSheet2(j, 4) = aSheet1(i, 2)
aSheet2(j, 5) = aSheet1(i, 1)
'now exit
Exit For
End If
End If
Next j
End If
Next i
'now output the second array
wsS2.Range("C3").Resize(lRowS2, 5) = aSheet2
End Sub
Desired result:
Naturally adjust the code where needed.
I hope this is what you are looking for..,
This example with assumption the content sheet2 and sheet1 as like as s/o sample, and sheet1 content is sorted by id:
Sub test()
Dim w_result As Worksheet
Dim w1 As Worksheet
Dim r As Long
Dim d As Long
Dim intLastRow As Long
Dim IntLastRow_Result As Long
Dim IntLastCol As Long
With ThisWorkbook
Set w1 = .Sheets("Sheet1")
Set w_result = .Sheets("Sheet2")
End With
With w1
intLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
IntLastRow_Result = w_result.Cells(Rows.Count, 3).End(xlUp).Row
arrNumber = .Range(.Cells(5, 1), .Cells(intLastRow, 3))
Dim LastID As String
LastID = ""
lastrow = 0
For r = 1 To UBound(arrNumber, 1)
If Len(arrNumber(r, 3)) > 0 Then
If arrNumber(r, 3) <> LastID Then
LastID = arrNumber(r, 3)
If arrNumber(r, 3) = "id1" Then lastrow = 4
If arrNumber(r, 3) = "id2" Then lastrow = 29
Else
lastrow = lastrow + 1
End If
If w_result.Range("E" & lastrow) = "Yes" Then
w_result.Range("F" & lastrow) = arrNumber(r, 1)
w_result.Range("G" & lastrow) = arrNumber(r, 2)
End If
End If
Next r
End With
End Sub
Anybody please help me figure my problem out?
Dim attPresent as Variant ' attpresent()
Set ws = thisworkbook.sheets("Sheet1")
lastrow = ws.cells(Rows.count, 8).end(xlup).row
attPresent = ws.Range("H4:H" & lastrow).Value 'errors if I use Dim attPresent() As Variant
For k = LBound(attPresent, 1) To UBound(attPresent, 1) ' Dim attPresent As Variant'errors if I use
msgbox attpresent(k,1)
Next
This line attPresent = ws.Range("H4:H" & lastrow).Value returns an error if I declare the variable as Dim attPresent() As Variant. Whereas, if declare the variable as Dim attPresent As Variant, this line For k = LBound(attPresent, 1) To UBound(attPresent, 1) errors.
Can anyone please help me clear this out?Thanks
As a good practice, try to remember to use Option Explicit, and also declare all your variables.
When you use Dim attPresent() As Variant to declare you array , and later on you insert values from a Range to your Array with attPresent = .Range("H4:H" & lastrow).Value, it will automatically Redim your array to 2-dimensinal array (1 to Row number, 1 to Column Number).
Option Explicit
Sub RngtoArray()
Dim attPresent() As Variant
Dim ws As Worksheet
Dim lastrow As Long
Dim k As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lastrow = .Cells(.Rows.Count, 8).End(xlUp).Row
attPresent = .Range("H4:H" & lastrow).Value
End With
For k = 1 To UBound(attPresent, 1)
MsgBox attPresent(k, 1)
Next
End Sub
Edit 1: A slightly different approach, in case there is only 1 cell in the Range:
With ws
lastrow = .Cells(.Rows.Count, 8).End(xlUp).Row
' for single column only - create a 1-Dimension array
ReDim attPresent(1 To lastrow - 4 + 1) ' when the Range starts from "H4"
For k = 1 To UBound(attPresent)
attPresent(k) = .Cells(4 + k - 1, "H")
Next k
End With
For k = 1 To UBound(attPresent)
MsgBox attPresent(k)
Next
I tried to separate the stuff that you had already defined but for clarity I thought I'd provide my full code:
Sub test()
Dim lastrow, i As Integer
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets("Sheet1")
Dim attPresent() As Variant
lastrow = ws.Cells(Rows.Count, "H").End(xlUp).Row
ReDim attPresent(lastrow - 4)
For i = 4 To lastrow
attPresent(i - 4) = ws.Range("H" & i).Value
Next
msg = Join(attPresent, " ")
MsgBox "The array holds: " & vbNewLine & msg
End Sub
I defined the array without a size to begin with then redefined it to the size it needs to be at a later stage once you know the lastrow (as you started on 4 i deducted 4 from lastrow).
I guessed the msgBox was to test what you had gathered so I created a dump that prints them all into one box but obviously change that if you have a lot of data. xD
To work with arrays I always loop through each individual entry, storing them one at a time. I'm not even sure whether you can dump an entire range into one in one step as I've never even looked into it. Anyway, I hope this solves your problem kupo.
Function RangeToArray(rng As Range)
Dim myArray() As Variant, ws As Worksheet
fr = rng.Row
fc = rng.Column
r = rng.Rows.Count
c = rng.Columns.Count
Set ws = rng.Worksheet
ReDim myArray(r - 1, c - 1)
For i = 0 To r - 1
For j = 0 To c - 1
myArray(i, j) = ws.Cells(fr + i, fc + j).Value2
Next j
Next i
RangeToArray = myArray
End Function
Sub f()
Dim rng As Range, attPresent() As Variant ' attpresent()
Set ws = ThisWorkbook.ActiveSheet 'Sheets("Sheet1")
lastrow = ws.Cells(Rows.Count, 8).End(xlUp).Row
Set rng = ws.Range("H4:H" & lastrow)
attPresent = RangeToArray(rng)
For k = LBound(attPresent, 1) To UBound(attPresent, 1) ' Dim attPresent As Variant'errors if I use
MsgBox attPresent(k, 0)
Next
End Sub
I created a more generic function that you can call in this specific case as well.