Loop through row and headers on separate sheets in VBA - vba

I have two sheets, sheet 1 has the data and sheet 2 has the headers. I am trying to fill sheet 2 row 2 with either a 1 or a 0 depending if the cell value in sheet 1 matches the header in sheet 2.
I have a range in sheet 1. I can loop through this range and fill in the first cell in sheet 2 row 2 with the correct information.
The next step is where I am struggling. I need to then move to the next header in sheet 2 but again loop through the range in sheet 1.
I tried nested for loops.
Sub LoopOne()
Dim sht As Worksheet
Dim NxtSht As Worksheet
Dim LastRow As Long
Dim LastCol As Long
Dim StartCell As Range
Dim StrtCell As Range
Dim ScndCell As Range
Set sht = Sheets("Sheet1")
Set NxtSht = Sheets("Sheet2")
Set StartCell = Range("A1")
Set StrtCell = NxtSht.Range("A1")
Set ScndCell = NxtSht.Range("A2")
sht.Activate
LastCol = sht.Cells(StartCell.Row, sht.Columns.Count).End(xlToLeft).Column
Range("A1").Select
sht.Range(StartCell, sht.Cells(LastCol)).Select
For Each cell In sht.Range(StartCell, sht.Cells(LastCol))
If cell.Value = Cells(StrtCell).Value Then
ScndCell.Value = 1
Else
If cell.Value <> Cells(StrtCell).Value Then GoTo escape
End If
escape:
NxtSht.Activate
Next cell
End Sub

Here is my quick stab at this using two nested for each x in y
Sub TestBed()
Dim myWB As Workbook, firstWS As Worksheet, secondWS As Worksheet, fHeaderRng As Range, sHeaderRng As Range, fOneCell As Range, sOneCell As Range
Dim fColUsed As Long, sColUsed As Long, debugStr As String
' Define Objects
Set myWB = ThisWorkbook
With myWB
Set firstWS = .Worksheets("Sheet1")
Set secondWS = .Worksheets("Sheet2")
End With
With firstWS
fColUsed = .UsedRange.Columns.Count
Set fHeaderRng = .Range(.Cells(1, 1), .Cells(1, fColUsed))
End With
With secondWS
sColUsed = .UsedRange.Columns.Count
Set sHeaderRng = .Range(.Cells(1, 1), .Cells(1, sColUsed))
End With
For Each sOneCell In sHeaderRng
For Each fOneCell In fHeaderRng
If fOneCell.Value = sOneCell.Value Then
debugStr = "Match || First Addr: " & fOneCell.Address & " || Second Addr: " & sOneCell.Address
Debug.Print debugStr
sOneCell.Offset(1, 0) = 1 ' 1 = Match
GoTo Next_sOneCell
Else
debugStr = "No Match || First Addr: " & fOneCell.Address & " || Second Addr: " & sOneCell.Address
Debug.Print debugStr
sOneCell.Offset(1, 0) = 0 ' 0 = No Match
End If
Next fOneCell
Next_sOneCell:
Next sOneCell
End Sub

Related

How to match two sets of cells on two sheets using vba

I am trying to match an ID cell from sheet 1 to and ID cell in sheet 2. If these match then I need to match a product cell from sheet 1 to a product cell in sheet two.
The ID cell in sheet 1 has multiples of the same ID in a column with different products in the next cell (column A = ID, column B = product).
In sheet 2 there is only one instance of each ID, however the product goes across the row. If the two criteria match, I need a 1 to be placed in the cell below the product.
This needs to be looped across the row and once the row finishes, move to the next ID in sheet 1. If the criteria do not match then the cell needs to be filled with a 0.
The trouble I am have is moving to the next ID. I have included the code and appreciate any help.
Public Sub test()
Dim ws As Worksheet, sh As Worksheet
Dim wsRws As Long, dataWsRws As Long, dataRng As Range, data_Rng As Range, data_cell As Range, datacell As Range
Dim shRws As Long, prodShRws As Long, resRng As Range, res_Rng As Range, results_cell As Range, product_cell As Range, shCols As Long
Set dataSht = Sheets("Device Import")
Set resSht = Sheets("Transform Pivot")
With dataSht
wsRws = .Cells(.Rows.Count, "A").End(xlUp).Row
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
dataWsRws = .Cells(.Rows.Count, "B").End(xlUp).Row
Set dataRng = .Range(.Cells(2, "A"), .Cells(wsRws, "A"))
Set data_Rng = .Range(.Cells(2, "B"), .Cells(wsRws, "B"))
End With
With resSht
shRws = .Cells(Rows.Count, "A").End(xlUp).Row
shCols = .Cells(1, Columns.Count).End(xlToLeft).Column
Set resRng = .Range(.Cells(2, "A"), .Cells(shRws, "A"))
Set res_Rng = .Range(.Cells(1, "B"), .Cells(1, shCols))
End With
i = 1
For Each data_cell In dataRng 'data sheet
For Each product_cell In res_Rng 'results sheet
For Each datacell In data_Rng 'data sheet
For Each results_cell In resRng 'results range
If data_cell = results_cell And datacell = product_cell Then
MsgBox data_cell.Value + " " + datacell.Value
results_cell.Offset(0, i) = 1 ' dcell = rcell so recell offset = 1
Else
MsgBox product_cell.Value + " " + results_cell.Value
results_cell.Offset(0, i) = 0 ' no match so rcell offset = 0
End If
If results_cell = "" Then
Exit For
End If
i = i + 1
Next results_cell ' Results ID column
i = 1
Exit For
Next datacell ' Data Product column cell
Next product_cell ' Results ID row
Next data_cell ' Data ID column cell
End Sub
An alternative approach would be
Initialize the resSht to 0's first
Loop only the dataSht looking at each ID Product pair
Use match to find the ID and product on resSht and fill in 1's as found
Public Sub Demo()
Dim dataSht As Worksheet, resSht As Worksheet
Dim rData As Range
Dim rwRes As Variant, clRes As Variant
Dim colResID As Long, rwResProd As Long
colResID = 1 '<-- Column in Result Sheet containing ID
rwResProd = 1 '<-- Row in Result Sheet containing Products
Set dataSht = Sheets("Device Import")
Set resSht = Sheets("Transform Pivot")
'Initialise to 0
With resSht
.Range(.Cells(rwResProd, .Columns.Count).End(xlToLeft).Offset(1, 0), _
.Cells(.Rows.Count, colResID).End(xlUp).Offset(0, 1)) = 0
End With
' Lookup each ID and Product pair from dataSht in resSht
With dataSht
For Each rData In .Range(.Cells(2, 1), .Cells(.Rows.Count, 1).End(xlUp))
rwRes = Application.Match(rData.Value2, resSht.Columns(colResID), 0)
If Not IsError(rwRes) Then
clRes = Application.Match(rData.Offset(0, 1).Value2, resSht.Rows(rwResProd), 0)
If Not IsError(clRes) Then
resSht.Cells(rwRes, clRes) = 1
Else
MsgBox "Product " & rData.Offset(0, 1).Value2 & " not found in Result Sheet", vbOKOnly + vbExclamation, "Missing Product"
End If
Else
MsgBox "ID " & rData.Value2 & " not found in Result Sheet", vbOKOnly + vbExclamation, "Missing ID"
End If
Next
End With
End Sub
Example Result

How to find value of cell above each cell

I want to screen all sheets for values that starts with "D"
In the sheets I formed blocks (1 column, 4 rows) with
- owner
- area
- parcel (that is allways starting with a "D")
- year of transaction (blocks of 1 column and 4 rows).
I want to make a summary in sheet "Test".
I'm able to find the parcel, but how can I get the info from the cell above?
Sub Zoek_kavels()
Dim ws As Worksheet
Dim rng As Range
Dim Area
Dim Kavel As String
rij = 1
For Each ws In ActiveWorkbook.Sheets
Set rng = ws.UsedRange
For Each cell In rng
If Left(cell.Value, 1) = "D" Then 'Starts with D
Sheets("Test").Cells(rij, 1) = cell.Value 'Kavel D..
Cells(cell.row - 1, cell.Column).Select
Area = ActiveCell.Value
Sheets("Test").Cells(rij, 2) = Area 'Oppervlakte
Sheets("Test").Cells(rij, 3) = ws.Name 'Werkblad naam
rij = rij + 1
End If
Next
Next
End Sub
A nice simple loop should do the trick, you may have had spaces in the worksheet, that would throw off the used range.
Here is a different approach.
Sub Get_CellAboveD()
Dim LstRw As Long, sh As Worksheet, rng As Range, c As Range, ws As Worksheet, r As Long
Set ws = Sheets("Test")
For Each sh In Sheets
If sh.Name <> ws.Name Then
With sh
LstRw = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rng = .Range("A1:A" & LstRw)
If LstRw > 1 Then
For Each c In rng.Cells
If Left(c, 1) = "D" Then
r = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1
ws.Range("A" & r).Value = c
ws.Range("B" & r).Value = c.Offset(-1).Value
ws.Range("C" & r).Value = sh.Name
End If
Next c
End If
End With
End If
Next sh
End Sub
There are two important points (and two not so important) to take care of your code:
start from row 2, because you are using .row - 1. Thus, if you start at row 1, row-1 would throw an error;
try to avoid Select, ActiveCell, etc.;(How to avoid using Select in Excel VBA);
write comments in English, not in Dutch (also good idea for variable names as well, rij or kavel do not help a lot);
declare the type of your variables, e.g. dim Area as String or as Long or anything else;
Option Explicit
Sub ZoekKavels()
Dim ws As Worksheet
Dim rng As Range
Dim Kavel As String
Dim rij As Long
Dim cell As Range
rij = 2 'start from the second row to avoid errors in .Row-1
For Each ws In ActiveWorkbook.Worksheets
Set rng = ws.UsedRange
For Each cell In rng
If Left(cell, 1) = "D" Then
With Worksheets("Test")
.Cells(rij, 1) = cell
.Cells(rij, 2) = ws.Cells(cell.Row - 1, cell.Column)
.Cells(rij, 3) = ws.Name
End With
rij = rij + 1
End If
Next
Next
End Sub
Or you can use .Cells(rij, 2) = cell.Offset(-1, 0) instead of Cells(cell.Row - 1, cell.Column), as proposed in the comments by #Shai Rado.

Extracting a data from one workbook and paste it in another

I have an excel file in drive "D".I would like to copy the data from workbook
"raw" from sheet1 to the another workbook "SC" in sheet "BW".
I am using the below code, to extract the data from one workbook and pasting it to another.
Sub extract()
Dim x As Workbook
Dim y As Workbook
Dim Val As Variant
Dim filename As String
Dim LastCell As Range
Dim LastRow As Long
CopyCol = Split("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X", ",")
LR = Cells(Rows.Count, 1).End(xlUp).Row
LC = Cells(1, Columns.Count).End(xlToLeft).Column
LCell = Selection.SpecialCells(xlCellTypeLastCell).Address
LCC = Selection.SpecialCells(xlCellTypeLastCell).Column
LCR = Selection.SpecialCells(xlCellTypeLastCell).Row
Set y = ThisWorkbook ' set ThisWorkbook object (where this code lies)
Set x = Workbooks.Open("D:\Student\Jenny\Raw.xlsx")
For Count = 0 To UBound(CopyCol)
Set temp = Range(CopyCol(Count) & "1:" & CopyCol(Count) & LCR)
If Count = 0 Then
Set CopyRange = temp
Else
Set CopyRange = Union(CopyRange, temp)
End If
Next
CopyRange.Copy
y.Sheets("BW").Range("A5").PasteSpecial
x.Close
End Sub
This code is workin, but the problem is , in my sheet "sheet1" I have my data starting from A4, and would like to copy the data in destination sheet "BW" from A5.
The current code, paste the copied data from A7. How can I modify such a way that it pastes the copied data from A5.
Any lead would be helpful.
In Set temp try 4 instead of 1 as
Set temp = Range(CopyCol(Count) & "1:" & CopyCol(Count) & LCR)
How I can select an particular sheet (Sheet Result) from source sheet. ?
Use
With x.Sheets("Result")
.
.
.
End With
or
x.Sheets("Result"). or whatever you are trying.
You have many unused and undeclared variables. Your updated code may look something like this:
Option Explicit
Sub extract()
Dim x As Workbook, y As Workbook
Dim temp As Range, CopyRange As Range
Dim LR As Long, LC As Long, LCR As Long, Count As Long
Dim copycol
copycol = Split("A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X", ",")
LR = Cells(Rows.Count, 1).End(xlUp).Row
LC = Cells(1, Columns.Count).End(xlToLeft).Column
Set y = ThisWorkbook ' set ThisWorkbook object (where this code lies)
Set x = Workbooks.Open("D:\Student\Jenny\Raw.xlsx")
With x.Sheets("Result")
LCR = .Cells(.Rows.Count, 1).End(xlUp).Row
For Count = 0 To UBound(copycol)
Set temp = .Range(copycol(Count) & "4:" & copycol(Count) & LCR)
If Count = 0 Then
Set CopyRange = temp
Else
Set CopyRange = Union(CopyRange, temp)
End If
Next
CopyRange.Copy
y.Sheets("BW").Range("A5").PasteSpecial
End With
x.Close
End Sub

Excel VBA Runtime Error '424' Object Required when deleting rows

I'm trying to compare cell values between 2 Sheets (Sheet1 & Sheet2) to see if they match, and if they match move the matching values in Sheet1 to a pre-existing list (Sheet3) and delete the values in Sheet1 afterwards.
I'm using the reverse For Loop in Excel VBA, but everything works until the part where I start deleting the row using newrange1.EntireRow.Delete.
This throws a '424' Object Required Error in VBA and I've spent hours trying to solve this, I'm not sure why this is appearing. Am I selecting the row incorrectly? The object?
Would appreciate if anyone can point me to the correct direction.
Here's my code:
Sub Step2()
Sheets("Sheet1").Activate
Dim counter As Long, unsubListCount As Long, z As Long, x As Long, startRow As Long
counter = 0
startRow = 2
z = 0
x = 0
' Count Sheet3 Entries
unsubListCount = Worksheets("Sheet3").UsedRange.Rows.Count
Dim rng1 As Range, rng2 As Range, cell1 As Range, cell2 As Range, newrange1 As Range
' Select all emails in Sheet1 and Sheet2 (exclude first row)
Set rng1 = Worksheets("Sheet1").Range("D1:D" & Worksheets("Sheet1").UsedRange.Rows.Count)
Set rng2 = Worksheets("Sheet2").Range("D1:D" & Worksheets("Sheet2").UsedRange.Rows.Count)
' Brute Loop through each Sheet1 row to check with Sheet2
For z = rng1.Count To startRow Step -1
'Cells(z, 4)
Set cell1 = Worksheets("Sheet1").Cells(z, "D")
For x = rng2.Count To startRow Step -1
Set cell2 = Worksheets("Sheet2").Cells(x, "D")
If cell1.Value = cell2.Value Then ' If rng1 and rng2 emails match
counter = counter + 1
Set newrange1 = Worksheets("Sheet1").Rows(cell1.Row)
newrange1.Copy Destination:=Worksheets("Sheet3").Range("A" & unsubListCount + counter)
newrange1.EntireRow.Delete
End If
Next
Next
End Sub
Here's the error I'm getting:
Your inner loop produces a lot of step-by-step work that is better accomplished with Application.Match. Your use of .UsedRange to retrieve the extents of the values in the D columns is better by looking for the last value from the bottom up.
Option Explicit
Sub Step2()
Dim z As Long, startRow As Long
Dim rng2 As Range, wk3 As Worksheet, chk As Variant
startRow = 2
z = 0
Set wk3 = Worksheets("Sheet3")
' Select all emails in Sheet1 and Sheet2 (exclude first row)
With Worksheets("Sheet2")
Set rng2 = .Range(.Cells(2, "D"), .Cells(.Rows.Count, "D").End(xlUp))
End With
With Worksheets("Sheet1")
For z = .Cells(.Rows.Count, "D").End(xlUp).Row To startRow Step -1
chk = Application.Match(.Cells(z, "D").Value2, rng2, 0)
If Not IsError(chk) Then
.Cells(z, "A").EntireRow.Copy _
Destination:=wk3.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0)
.Cells(z, "A").EntireRow.Delete
End If
Next
End With
End Sub
As noted by Ryan Wildry, your original problem was continuing the loop and comparing after deleting the row. This can be avoided by adding Exit For after newrange1.EntireRow.Delete to jump out of the inner loop once a match was found. I don't think you should 'reset cell1' as this may foul up the loop iteration.
I think what's happening is when you are deleting the row, you are losing the reference to the range Cell1. So I reset this after the deletion is done, and removed the reference to newRange1. Give this a shot, I have it working on my end. I also formatted the code slightly too.
Option Explicit
Sub Testing()
Dim counter As Long: counter = 0
Dim z As Long: z = 0
Dim x As Long: x = 0
Dim startRow As Long: startRow = 2
Dim Sheet1 As Worksheet: Set Sheet1 = ThisWorkbook.Sheets("Sheet1")
Dim Sheet2 As Worksheet: Set Sheet2 = ThisWorkbook.Sheets("Sheet2")
Dim Sheet3 As Worksheet: Set Sheet3 = ThisWorkbook.Sheets("Sheet3")
Dim rng1 As Range: Set rng1 = Sheet1.Range("D1:D" & Sheet1.UsedRange.Rows.Count)
Dim rng2 As Range: Set rng2 = Sheet2.Range("D1:D" & Sheet2.UsedRange.Rows.Count)
Dim unsubListCount As Long: unsubListCount = Sheet3.UsedRange.Rows.Count
Dim cell1 As Range
Dim cell2 As Range
Dim newrange1 As Range
' Brute Loop through each Sheet1 row to check with Sheet2
For z = rng1.Count To startRow Step -1
Set cell1 = Sheet1.Cells(z, 4)
For x = rng2.Count To startRow Step -1
Set cell2 = Sheet2.Cells(x, 4)
If cell1 = cell2 Then
counter = counter + 1
Set newrange1 = Sheet1.Rows(cell1.Row)
newrange1.Copy Destination:=Sheet3.Range("A" & unsubListCount + counter)
newrange1.EntireRow.Delete
Set newrange1 = Nothing
Set cell1 = Sheet1.Cells(z, 4)
End If
Next
Next
End Sub

Vba copy row to another workbook based on condition

I have 2 wb and need to copy value to another wb based on condition:
If the value in the column F of wb2 appears in column F of wb1, then I need to copy value in the column G of wb2 to column G of wb1. The code is below:
Dim LtRow As Long
Dim m As Long, n As Long
With wb2.Worksheets.Item(1)
LtRow = .Cells(.Rows.Count, "G").End(xlUp).Row
End With
With ThisWorkbook.Sheets.Item(2)
n = .Cells(.Rows.Count, "G").End(xlUp).Row + 1
End With
For m = 1 To LtRow
With wb2.Worksheets.Item(1)
If .Cells(m, 6).Value = ThisWorkbook.Sheets.Item(2).Cells(m, 6).Value Then
.Rows(m).Copy Destination:=ThisWorkbook.Sheets.Item(2).Range("G" & n)
n = n + 1
End If
End With
Next m
I don't know why the code didn't work at all! Where is the problem in my code?
EDIT:
To see what your excel files look like wasn't an option for what you are trying to do. Especially because in you have many empty rows. Anyway, this works for me:
Sub CopyConditions()
Dim Wb1 As Workbook
Dim Wb2 As Workbook
Dim Wb1Ws2 As Worksheet
Dim Wb2Ws1 As Worksheet
Set Wb1 = ThisWorkbook
Set Wb1Ws2 = ThisWorkbook.Sheets("Differences")
'open the wb2
Dim FullFilePathAndName As Variant
Dim StrOpenFileTypesDrpBx As String
Let StrOpenFileTypesDrpBx = "xls (*.xls),*.xls,CSV (*.CSV),*.CSV,Excel (*.xlsx),*.xlsx,OpenOffice (*.ods),*.ods,All Files (*.*),*.*,ExcelMacros (*.xlsm),.xlsm"
Let FullFilePathAndName = Application.GetOpenFilename(StrOpenFileTypesDrpBx, 1, "Compare this workbook ""(" & Wb1.Name & ")"" to...?", , False) 'All optional Arguments
If FullFilePathAndName = False Then
MsgBox "You did't select a file!", vbExclamation, "Canceled"
Exit Sub
Else
Set Wb2 = Workbooks.Open(FullFilePathAndName)
Set Wb2Ws1 = Wb2.Sheets("Sheet1")
End If
Dim rCell As Range
Dim sCell As Range
'loop through each cell in column F until row30 because with the empty cells in the column we can't use Rows.count
For Each rCell In Wb1Ws2.Range(Wb1Ws2.Cells(1, 6), Wb1Ws2.Cells(30, 6)) 'Wb1Ws2.Cells(Wb1Ws2.Rows.Count, 6).End(xlUp))
'if the cell column F is equal to a cell in wb2 sheet1 column L
For Each sCell In Wb2Ws1.Range(Wb2Ws1.Cells(3, 12), Wb2Ws1.Cells(Wb2Ws1.Rows.Count, 12).End(xlUp))
If sCell = rCell Then
rCell.Offset(0, 1) = sCell.Offset(0, 1)
End If
Next sCell
Next rCell
End Sub
How does it go for you?