VBA Copy row when / if values match *Brain Fried* - vba

I have a workbook with 2 sheets: Masterlist (old data) and Results (new data) with unique identifiers in column A.
I'm trying to find a way to copy the row containing the most recent data from the Results tab onto the matching row in the Masterlist sheet
I have only been able to find a way to copy the new data at the bottom of the Masterlist
Sub UpdateML()
Dim wM As Worksheet, wR As Worksheet
Dim r1 As Range, r2 As Range
Dim cel1 As Range, cel2 As Range
Dim LastRow As Long
Application.ScreenUpdating = False
Set wM = ThisWorkbook.Worksheets("MasterList")
Set wR = ThisWorkbook.Worksheets("Results")
With wM
Set r1 = .Range("A1", .Cells(.Rows.Count, .Columns("A:A").Column).End(xlUp))
End With
With wR
Set r2 = .Range("A1", .Cells(.Rows.Count, .Columns("A:A").Column).End(xlUp))
End With
On Error Resume Next
For Each cel1 In r1
With Application
Set cel2 = .Index(r2, .Match(cel1.Value, r2, 0)) 'find match in Masterlist
If Err = 0 Then
copyResult cel2 'copy result to masterlist
End If
Err.Clear
End With
Next cel1
End Sub
Sub copyResult(cel As Range)
Dim w As Worksheet, r As Range
Set w = ThisWorkbook.Worksheets("Masterlist")
Set r = w.Cells(w.Rows.Count, Columns("A:A").Column).End(xlUp).Offset(1) 'next row
cel.EntireRow.Copy w.Cells(r.Row, 1)
End Sub

Your copyResult method (when setting the value of r) is picking up the bottom row + 1, so that's why it's being dumped at the bottom of the list.
You have a LastRow variable in the UpdateML method though, which is unused.
I've got it to work by using that as a counter variable to track the row index and passing that into the copyResult method. Like this:
Sub UpdateML()
Dim wM As Worksheet, wR As Worksheet
Dim r1 As Range, r2 As Range
Dim cel1 As Range, cel2 As Range
Dim LastRow As Long
Application.ScreenUpdating = False
Set wM = ThisWorkbook.Worksheets("MasterList")
Set wR = ThisWorkbook.Worksheets("Results")
With wM
Set r1 = .Range("A1", .Cells(.Rows.Count, .Columns("A:A").Column).End(xlUp))
End With
With wR
Set r2 = .Range("A1", .Cells(.Rows.Count, .Columns("A:A").Column).End(xlUp))
End With
LastRow = 1
On Error Resume Next
For Each cel1 In r1
With Application
Set cel2 = .Index(r2, .Match(cel1.Value, r2, 0)) 'find match in Masterlist
If Err = 0 Then
copyResult cel2, LastRow 'copy result to masterlist
End If
Err.Clear
LastRow = LastRow + 1
End With
Next cel1
End Sub
Sub copyResult(cel As Range, row As Long)
Dim w As Worksheet
Set w = ThisWorkbook.Worksheets("Masterlist")
cel.EntireRow.Copy w.Cells(row, 1)
End Sub
I'm a little rusty with VBA (not used it in around a year), so there may be more elegant solutions, but this is definitely one option.

Here is a different approach. It uses "Find" which is a little leaner. It also uses activesheets and cells rather than references.
Do you need to add missing items from Results to the Master list? This covers that. If Results col A is the same as MasterList col A this will also work
Sub itworks()
'''covers the above
On Error Resume Next ''Can change this to more preferred if error <> 0
Sheets("MasterList").Range("a1").Select
lo = Range("A" & Range("A:A").Rows.Count).End(xlUp).Offset(1).Address
Do Until ActiveCell.Address = lo
Sheets("Results").Range("A:A").Find(ActiveCell.Value).EntireRow.Copy ActiveCell''copies found row to your Active Cell
ActiveCell.Offset(1).Select
Loop
''Adds missing rows
Sheets("Results").Activate
Range("a1").Select
lo2 = Range("A" & Range("A:A").Rows.Count).End(xlUp).Offset(1).Address
Do Until ActiveCell.Address = lo2
Set missing = Sheets("MasterList").Range("A:A").Find(ActiveCell.Value)
If missing Is Nothing Then
ActiveCell.EntireRow.Copy Sheets("MasterList").Range("a1").End(xlDown).Offset(1)
End If
ActiveCell.Offset(1).Select
Loop
End Sub

Related

Using (Nested Loop) .find to grab identical values seperate sheets VBA EXCEL

I have the following code which loops through two different worksheets and compares column A to column A checking if the same value is on the other sheet. If it is then the row is colored in green.
Dim compareRange As Range
Dim toCompare As Range
Dim rFound As Range
Dim cel As Range
Set compareRange = Worksheets("sheet2").Range("A1:A" & Lastrow3)
Set toCompare = Worksheets("sheet3").Range("A1:A" & Lastrow4)
Set rFound = Nothing
For Each cel In toCompare
Set rFound = compareRange.Find(cel)
If Not rFound Is Nothing Then
cel.EntireRow.Interior.Color = 5296274
Set rFound = Nothing
End If
Next cel
Now that I have the cell with the row how do I grab the cells from the same row but on different column? because now I want to check if column L from sheet2 matches column L from sheet3. If it doesn't I want to be grab that value from sheet2 and put it in a new row below on in the same column L. Any guidance or help would be appreciated.
This should help demostrate how to do what youre after
Private Sub compAre()
Application.ScreenUpdating = False
Dim sht1 As Range
Dim rcell As Range
Set sht1 = ThisWorkbook.Sheets("Sheet1").Range("A1:A3")
For Each rcell In sht1.Cells
If rcell.Value = ThisWorkbook.Sheets("Sheet2").Range("L" & rcell.Row).Value Then
sht1.Rows.Interior.Color = vbBlue
End If
Next rcell
Application.ScreenUpdating = True
End Sub
Here's some code that covers most of what you describe, coloring the cells that match and putting those into column L of the 3rd sheet. I didn't understand the remainder of the question after that, but this should give you a good start. The animation starts by showing the contents of sheets 1,2,3 and then shows those sheets again after running the macro.
Option Explicit
Sub test()
Dim sh1 As Worksheet, sh2 As Worksheet, sh3 As Worksheet, num As Integer
Dim r1 As Range, r2 As Range, r3 As Range, cell1 As Range, cell2 As Range
Set sh1 = Worksheets("1")
Set sh2 = Worksheets("2")
Set sh3 = Worksheets("3")
Set r1 = Range(sh1.Range("A1"), sh1.Range("A1").End(xlDown))
Set r2 = Range(sh2.Range("A1"), sh2.Range("A1").End(xlDown))
Set r3 = sh3.Range("L1")
For Each cell1 In r1
For Each cell2 In r2
If cell1 = cell2 Then
cell1.Interior.Color = vbGreen
cell2.Interior.Color = vbGreen
r3 = cell1.Value
Set r3 = r3.Offset(1, 0)
num = num + 1
End If
Next
Next
MsgBox (num & " were found to match")
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 TreeView_NodeCheck: Search for Match in Column and post in row beneath if empty

I am trying to work around the tough task to save selected nodes from a TreeView (tough due to my limited VBA knowledge) by first saving a UserID with a TextBox_AfterUpdate Event and subsequently saving the checked node's full path in the rows below when a match is found. Multiple checks are possible, that's why I approached it the way I did below.
I am working on this Problem for 2 working-days now and pray that one of you can help me out of my misery here haha
No Error is produced and a look at the Debugger Shows me that each column in found correctly. Also, the saving user ID via TextBox_AfterUpdate Event works like a treat and should stay this way. It simply does not copy it - please Help.
Thank you in advance!
Private Sub SuppNo_AfterUpdate()
'########Save SuppNo for CG-entry-saving########
Dim wb As Workbook
Set wb = ActiveWorkbook
Dim ws As Worksheet
Set ws = wb.Worksheets("Supplier Skills")
Dim lastcol As Long
With ws
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
'Copy Supplier No into Row 1 and next empty column
ws.Cells(1, lastcol).Offset(0, 1).Value = Me.SuppNo.Value
End Sub
'------------------------------------------------------------------------------------------
Private Sub CGTreeView_NodeCheck(ByVal Node As MSComctlLib.Node)
Dim wb As Workbook
Set wb = ActiveWorkbook
Dim ws As Worksheet
Set ws = wb.Worksheets("Supplier Skills")
Dim myNode As Node
'1. - - Copy Supplier No into Row 1 and next empty column
'Done in SuppNo_Change event
'2.1. - - Find matching entry
Dim aCell As Range
Dim col As Long, lRow As Long, i As Long
Dim colName, NodePath As String
strFind = Me.SuppNo
NodePath = Me.CGTreeView.SelectedItem.FullPath
With ws
Set aCell = .Range("A1:ZZ1").Find(What:=Me.SuppNo, LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False)
'~~> If Found
If Not aCell Is Nothing Then
col = aCell.Column
colName = Split(.Cells(, col).Address, "$")(1)
lRow = .Range(colName & .Rows.Count).End(xlUp).Row
'2.2. - - Find the last empty row and copy each new FullPath when checked
For i = 2 To 50
If Cells(i, col) Is Nothing Then
ws.Cells(i, col) = NodePath
i = i + 1
End If
Next i
'~~> If not found
Else
Exit Sub
End If
End With
End Sub
I added a few features to eliminate duplicates.
Private Sub SuppNo_AfterUpdate()
'########Save SuppNo for CG-entry-saving########
Dim IDColumn As Long
Dim dItems As Object
Dim c As Range, ItemsRange As Range
Dim n As Node
Set dItems = CreateObject("Scripting.Dictionary")
With ActiveWorkbook.Worksheets("Supplier Skills")
IDColumn = getSuppNoColumn
.Cells(1, IDColumn).Value = Me.SuppNo.Value
Set ItemsRange = .Range(.Cells(2, IDColumn), .Cells(.Rows.count, IDColumn).End(xlUp))
If Not ItemsRange Is Nothing Then
For Each c In ItemsRange
dItems(c.text) = vbNullString
Next
End If
End With
For Each n In CGTreeView.Nodes
n.Checked = dItems.exists(n.FullPath)
Next
End Sub
'------------------------------------------------------------------------------------------
Private Sub CGTreeView_NodeCheck(ByVal Node As MSComctlLib.Node)
Dim IDColumn As Long
Dim dItems As Object
Dim n As Node
If Me.SuppNo.Value <> "" Then
IDColumn = getSuppNoColumn
With ActiveWorkbook.Worksheets("Supplier Skills")
.Range(.Cells(1, IDColumn), .Cells(.Rows.count, IDColumn).End(xlUp)).Offset(1).Clear
Set dItems = CreateObject("Scripting.Dictionary")
For Each n In CGTreeView.Nodes
If n.Checked Then dItems(n.FullPath) = vbNullString
Next
If dItems.count > 0 Then .Cells(2, IDColumn).Resize(dItems.count) = Application.Transpose(dItems.Keys)
End With
End If
End Sub
Function getSuppNoColumn() As Long
Dim f As Range
With ActiveWorkbook.Worksheets("Supplier Skills")
Set f = .Range("1:1").Find(What:=Me.SuppNo, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If f Is Nothing Then
getSuppNoColumn = IIf(.Cells(1, 1) = "", 1, .Cells(1, .Columns.count).End(xlToLeft).Offset(0, 1).Column)
Else
getSuppNoColumn = f.Column
End If
End With
End Function

Compare and copy the match column into the next column using macro

I'm just new to macros and have stuck with a problem. Well I got a macro file on the web that compare the two sheets and copies the found match into the 1st row.
Sub test()
Dim rng2 As Range, c2 As Range, cfind As Range
Dim x, y
With Worksheets("sheet1")
Set rng2 = Range(.Range("B2"), .Range("B2").End(xlDown))
For Each c2 In rng2
x = c2.Value
With Worksheets("sheet2").Columns("b:B")
On Error Resume Next
Set cfind = .Cells.Find(what:=x, lookat:=xlWhole)
If cfind Is Nothing Then GoTo line1
y = cfind.Offset(0, -1).Value
End With
c2.Offset(0, -1) = y
line1:
Next c2
End With
End Sub
However I didn't understand how is it exactly working? I mean I just want to reverse the existing logic. I mean I want to populate the found match in next row to the data in C column not in A.
I attempted modifying the Offset index values it didn't work. Can anyone please help me out solving this.
Thanks !
Sub test()
Dim rSh1 As Range, rSh2 As Range, rFound As Range, r As Range
With Worksheets("sheet1")
Set rSh1 = .Range("B2", .Range("B2").End(xlDown))
End With
With Worksheets("sheet2")
Set rSh2 = .Columns("b:B")
End With
For Each r In rSh1
With r
Set rFound = rSh2.Find(what:=.Value, lookat:=xlWhole)
If Not rFound Is Nothing Then
.Offset(0, 1) = rFound.Offset(0, -1).Value
End If
End With
Next r
End Sub

Check merged cell and compare adjacent to set unique value from compared cells values

I'm writing a macro in Excel 2010 for a problem that is as follows:
I have two columns, one with a Key string value and one with a uuid. The idea is that every key should have only one uuid but as the table is now, key cell could be merged cells or single cells.
The macro needs to recognize which cells are merged and which are not, so, I have two options:
If cell is merged, check all its adjacent cells, pick first uuid value and copy/paste it to other adjacent cells, that is to say, cell below(Could be with an Offset())
If cell is not merged , but key value is repeated in multiple cells, copy/paste uuid value to adjacent cells.
So basically is to check merged cells MergeArea but I don't know if I need to iterate through its addresses or check cells in the range with an offset of Offset(0,1) or what.
With my code I can know if the cells are merged but now, how con I iterate through it's adjacent cells values?
Code as is now:
Sub CopyUUID()
Dim lRow As Long
Dim rng As Range
Dim ws As Worksheet
Dim rMerged As Range
Dim value As Variant
Set ws = Sheets(ActiveSheet.Name)
On Error GoTo ExitProgram 'If an error happens within the execution, skips it and continue in next step
Application.DisplayAlerts = False 'We can cancel the procedure without errors
With ws
lRow = .Range("F" & .Rows.count).End(xlUp).row
Set rng = .Range(.Cells(3, 6), .Cells(lRow, 6))
rng.Select
For Each cell In rng
If cell.MergeCells Then
'Code for merged cells
Else
'Code to use for single cells
End If
Next cell
End With
ExitProgram:
Exit Sub
End Sub
Option Explicit
Sub CopyUUID()
Const UUID As Long = 31 'col AE
Dim lRow As Long, cel As Range, isM As Boolean, copyID As Boolean, kCol As Long
With ActiveSheet
kCol = -25 'col F
lRow = .Cells(.Rows.Count, UUID + kCol).End(xlUp).Row
For Each cel In .Range(.Cells(3, UUID), .Cells(lRow, UUID))
isM = cel.Offset(0, kCol).MergeCells
copyID = isM And Len(cel.Offset(0, kCol)) = 0
copyID = copyID Or (Not isM And cel.Offset(0, kCol) = cel.Offset(-1, kCol))
If copyID Then cel = cel.Offset(-1)
Next
End With
End Sub
Try the following code. Note that this is going to overwrite the current contents of UUID, so make a backup copy before testing. If you don't want the UUID column modified, you can modify this to suit your needs.
Sub CopyUUID()
Dim lRow As Long
Dim rng As Range
Dim c As Range
Dim ws As Worksheet
Dim rMerged As Range
Dim value As Variant
Set ws = Sheets(ActiveSheet.Name)
On Error GoTo ExitProgram 'If an error happens within the execution, skips it and continue in next step
' Application.DisplayAlerts = False 'We can cancel the procedure without errors
With ws
lRow = .Range("F" & .Rows.Count).End(xlUp).Row
Set rng = .Range(.Cells(3, 6), .Cells(lRow, 6))
' rng.Select
For Each c In rng
If c.MergeCells Then
'Code for merged cells
c.Offset(0, 1).Formula = c.MergeArea.Cells(1, 1).Offset(0, 1).Formula
Else
'Code to use for single cells
If c.Formula = c.Offset(-1, 0).Formula Then
c.Offset(0, 1).Formula = c.Offset(-1, 1).Formula
End If
End If
Next c
End With
ExitProgram:
Exit Sub
End Sub
When in a MergedCell, it makes the UUID the same as the UUID of the first cell in the merged area. When not in a MergedCell, it copies UUID from the row above if Key is the same as the row above.
I changed your variable cell to c (I don't like to use variable names that can be confused with built-ins) and commented out a couple of lines.
Hope this helps
I adopt a simple approach to this problem as illustrated through steps taken by me.
sample sheet showing data with merged cells and unmerged cells.
Run the program code to unmerge the cells. Output of the program is appended below.
If this structure of data matches your case then addition of 2 lines of code for column B will leave the data as per following image.
Program code is as follows:
'Without column deletion:
Sub UnMergeRanges()
Dim cl As Range
Dim rMerged As Range
Dim v As Variant
For Each cl In ActiveSheet.UsedRange
If cl.MergeCells Then
Set rMerged = cl.MergeArea
v = rMerged.Cells(1, 1)
rMerged.MergeCells = False
rMerged = v
End If
Next
End Sub
'With coumn deletion
Sub UnMergeRangesB()
Dim cl As Range
Dim rMerged As Range
Dim v As Variant
For Each cl In ActiveSheet.UsedRange
If cl.MergeCells Then
Set rMerged = cl.MergeArea
v = rMerged.Cells(1, 1)
rMerged.MergeCells = False
rMerged = v
End If
Next
Columns("B:B").Select
Selection.Delete Shift:=xlToLeft
End Sub