Compare two columns, Clear duplicate cells - excel

I am trying to compare two columns (A and B) for duplicates. As an output I am trying to get cells that does not match (not duplicates). Column A values are coming from table 1 and Column B values are coming from table 2. Code target is basically to get to know which items were deleted from table 2 (Column B).
Data looks like:
A B
BMW PORSCHE
FIAT VOLVO
VOLVO AUDI
PORSCHE FERRARI
FERRARI TOYOTA
TOYOTA
AUDI
Output should be:
A B
BMW
FIAT
This is working for highlighting duplicates, but how to get values deleted that are duplicates? For example using .ClearContents. Then after that I have loop for deleting empty rows in range.
Sub MarkDuplicatesInCompare()
Dim ws As Worksheet
Dim cell As Range
Dim myrng As Range
Dim clr As Long
Dim lastCell As Range
Dim EntireRow As Range
Set ws = ThisWorkbook.Sheets("Compare")
Set myrng = ws.Range(ws.Cells(2, 1), ws.Cells(ws.Rows.Count, "B").End(xlUp))
With myrng
Set lastCell = .Cells(.Cells.Count)
End With
myrng.Interior.ColorIndex = xlNone
clr = 3
For Each cell In myrng
If Application.WorksheetFunction.CountIf(myrng, cell) > 1 Then
If myrng.Find(what:=cell, lookat:=xlWhole, MatchCase:=False, after:=lastCell).Address = cell.Address Then
cell.Interior.ColorIndex = clr
clr = clr
Else
cell.Interior.ColorIndex = myrng.Find(what:=cell, lookat:=xlWhole, MatchCase:=False, after:=lastCell).Interior.ColorIndex
End If
End If
Next
' Delete empty rows
For I = myrng.Rows.Count To 1 Step -1
Set EntireRow = myrng.Cells(I, 1).EntireRow
If Application.WorksheetFunction.CountA(EntireRow) = 0 Then
EntireRow.Delete
End If
Next
End Sub

Give this a try:
Sub Keanup()
Dim i As Long, j As Long, Na As Long, Nb As Long
Na = Cells(Rows.Count, "A").End(xlUp).Row
Nb = Cells(Rows.Count, "B").End(xlUp).Row
For i = Na To 1 Step -1
v = Cells(i, "A").Value
For j = Nb To 1 Step -1
If v = Cells(j, "B").Value Then
Cells(i, "A").Delete shift:=xlUp
Cells(j, "B").Delete shift:=xlUp
Exit For
End If
Next j
Next i
End Sub
Note we run the loops bottom up.

you could use AutoFilter()
With Range("A1", Cells(Rows.Count, 1).End(xlUp))
.Rows(1).EntireRow.Insert ' insert temporary row for dummy headers
With .Offset(-1).Resize(.Rows.Count + 1)
.Range("A1:B1").Value = Array("h1", "h2") ' write dummy headers
.AutoFilter field:=1, Criteria1:=Application.Transpose(Range("B1", Cells(Rows.Count, 2).End(xlUp)).Value), Operator:=xlFilterValues
.Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).ClearContents
.Parent.AutoFilterMode = False
.Rows(1).EntireRow.Delete ' remove dummy headers temporary row
End With
End With
Range("B1", Cells(Rows.Count, 2).End(xlUp)).ClearContents ' clear column B values
or with Find()
Dim cel As Range
With Range("B1", Cells(Rows.Count, 2).End(xlUp))
For Each cel In Range("A1", Cells(Rows.Count, 1).End(xlUp))
If Not .Find(what:=cel.Value, LookIn:=xlValues, lookat:=xlWhole) Is Nothing Then cel.ClearContents
Next
.ClearContents
End With
which, should keeping "surivors" at the top be an issue, becomes:
Dim cel As Range, s As String
With Range("B1", Cells(Rows.Count, 2).End(xlUp))
For Each cel In Range("A1", Cells(Rows.Count, 1).End(xlUp))
If Not .Find(what:=cel.Value, LookIn:=xlValues, lookat:=xlWhole) Is Nothing Then s = s & cel.Address(False, False) & " "
Next
.ClearContents
End With
If s <> vbNullString Then Range(Replace(Trim(s), " ", ",")).Delete xlUp

Related

Excel VBA - Find and copy non-matching rows to another worksheet

I would like to compare 2 columns in the same worksheet, search for non-matching values in column A when compared to column D and copy the entire rows of these non-matching values in column A to another worksheet.
Here is a sample of the worksheet:
Therefore, I would like to compare column A with column D, find the values which do not match and copy the entire corresponding rows from Columns A and B to a new worksheet.
*Edit, I forgot to include my code
Dim CopyToRow As Integer
Dim rng1 As Range
Dim rng2 As Range
Dim cell As Range
Dim found As Range
'Start copying data to row 2 in Sheet2 (row counter variable)
CopyToRow = 2
Set rng1 = Range(ActiveSheet.Cells(2, 1), ActiveSheet.Cells(2, 1).End(xlDown))
Set rng2 = Range(ActiveSheet.Cells(4, 2), ActiveSheet.Cells(4, 2).End(xlDown))
For Each cell In rng1
Set found = rng2.Find(what:=cell, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not found Is Nothing Then
cell.EntireRow.Copy Destination:=Sheets("Sheet2").Range("A" & CopyToRow)
CopyToRow = CopyToRow + 1
End If
Next cell
Many thanks and much appreciated!
I agree with Ron Rosenfeld that you should have demonstrated your own attempt. That being said, perhaps this will be of some help to you. Not the most elegant but should work provided you update references to your own sheet names.
Sub SOPractice()
Dim SearchCell As Range 'each value being checked
Dim SearchRng As Range 'column A
Dim LastRow As Long
Dim MatchFound As Range
Dim i As Long: i = 1
LastRow = YourSheet.Range("A" & Rows.Count).End(xlUp).Row
With YourSheet
Set SearchRng = .Range(.Cells(2, 1), .Cells(LastRow, 1))
Application.ScreenUpdating = False
For Each SearchCell In SearchRng
Set MatchFound = .Range("D:D").Find _
(What:=SearchCell.Value, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=False)
If MatchFound Is Nothing Then 'No match hence copy to other sheet
.Range(SearchCell.Address, SearchCell.Offset(, 1)).Copy
YourCopyToSheet.Cells(i, 1).PasteSpecial xlPasteAll
i = i + 1
End If
Next SearchCell
End With
Application.ScreenUpdating = True
Application.CutCopyMode = False
End Sub
I have also found a solution, using a Dictionary object:
Dim Cl As Range, Rng As Range, Dic As Object
Set Dic = CreateObject("scripting.dictionary")
With Dic
For Each Cl In MyWorksheet1Name.Range("D2", MyWorksheet1Name.Range("D" & Rows.Count).End(xlUp))
.Item(Cl.Value) = Empty
Next Cl
For Each Cl In MyWorksheet1Name.Range("A2", MyWorksheet1Name.Range("A" & Rows.Count).End(xlUp))
If Not .Exists(Cl.Value) Then
If Rng Is Nothing Then Set Rng = Cl Else Set Rng = Union(Rng, Cl)
End If
Next Cl
End With
If Not Rng Is Nothing Then
Rng.EntireRow.Copy MyWorksheet2Name.Range("A" & Rows.Count).End(xlUp)
End If
Cheers!

Loop through rows then columns in Excel

I have a range of data in excel which is uniform, Column A has a description, Column B has a unique ID and column C is blank. Then the next 3 columns have the same set of data. I am trying to get my vba loop to go down the two sets of data and compare the unique ID's, if there are differences it copies it to the range of data to a new sheet.
The issue is it is not just 6 columns of data its about a couple of hundred, so once the final row has been reached after checking the first range of data, the loop needs to move over 6 columns to begin the process again.
I am having some difficulty getting the loop to move across 6 columns once the finalrow has been reached
Sub finddata()
Dim s As Worksheet
Dim uniqueId As String
Dim finalrow As Long
Dim i As Long
Dim c As Long
Dim rngSearch As Range
Dim rngFound As Range
Dim finalcolumn As Long
Dim offset As Integer
Application.ScreenUpdating = True
uniqueId = Sheets("Data").Range("B2").Value
finalrow = Sheets("Data").Range("G100000").End(xlUp).Row
finalcolumn = Sheets("Data").Range("XFD1").End(xlToLeft).Column
offset = 3
Set s = Sheets("Data")
Set rngSearch = s.Range(s.Cells(2, 5), s.Cells(finalrow, 5))
Sheets("DataValidation").Range("A1:C100000").ClearContents
If i = finalrow GoTo 'guessing this is how to being to loop to move over columns
For i = 2 To finalrow
uniqueId = s.Cells(i, 2).Value
Set rngFound = rngSearch.Find(What:=uniqueId, LookIn:=xlValues, LookAt:=xlWhole)
If rngFound Is Nothing Then
s.Range(Cells(i, 1), Cells(i, 6)).Copy
Sheets("DataValidation").Range("A1048575").End(xlUp).offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
End If
Next i
MsgBox "Done"
End Sub
Try that one
For j = 1 to finalcolumn step 6
Set rngSearch = s.Range(s.Cells(2, j - 1 + 5), s.Cells(finalrow, j - 1 + 5))
For i = 2 To finalrow
uniqueId = s.Cells(i, 2 + j - 1).Value
Set rngFound = rngSearch.Find(What:=uniqueId, LookIn:=xlValues, LookAt:=xlWhole)
If rngFound Is Nothing Then
s.Range(Cells(i, 1 + j - 1), Cells(i, 6 + j - 1)).Copy
Sheets("DataValidation").Range("A1048575").End(xlUp). _
offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
End If
Next i
Next j
Hope it helps
Here is a short example of how you can iterate on uniform ranges of 3 columns, and moving right by offset of 6. You said your ranges are uniform so I hard-coded the initial value of rngSet.
In the For Each I'm printing the address of the cells but you can do your checks there. The While loop will end when it reaches an empty cell.
Dim rngSet As Range, rngRow As Range
Set rngSet = Range("A1:B6")
While Not IsEmpty(rngSet.Cells(1, 1).Value)
For Each rngRow In rngSet.Rows
Debug.Print rngRow.Cells(1, 1).Address
Next rngRow
Set rngSet = rngSet.Offset(0, 6)
Wend

My code won’t delete lines of unnecessary data under the data I’m trying to keep

Sub DeleteExtraValues ()
Dim I as Integer, strValueToFind As String, lngRows As Long, she As Worksheet
Set an = ThisWorkbook.ActiveSheet
LngRows = sh.Range(“A1048576”).End(xlUp).Row
strValueToFind = “DCAP”
For I = 1 To lngRows
If InStr(Cells(I,1).Value, strValueToFind) = 0 Then
If Cells(I,1).Value = “” Then
Else
Rows(I).Delete
I = I-1
End If
End If
Next I
End Sub
When running this, it will delete the cells above the data I want to keep and then will stop once it gets to the first cell that contains “DCAP”. I need it also to delete any unnecessary information after the last cell that contains “DCAP”.
Try this code. It removes every row that doesn't contain DCAP in the first column.
Dim r As Long
Dim LastRow As Long
r = 1
LastRow = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row
Do Until r > LastRow
DoEvents
If InStr(1, Cells(r, 1), "DCAP") > 0 Then
r = r + 1
Else
Cells(r, 1).EntireRow.Delete
LastRow = LastRow - 1
End If
Loop
MsgBox
"done"
Try this...
Dim rng As Range
Set rng = ActiveSheet.Range("A1").CurrentRegion 'depending on your data you may have to change to a specific range using last row and column
' change the `Field` to the column that contains "DCAP"
With rng
.AutoFilter Field:=9, Criteria1:="<>DCAP", Operator:=xlAnd 'select all cells that are not "DCAP"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete 'don't delete the header row
.AutoFilterMode = False
End With

VBA - Vlookup Multiple Columns and Fill to End of Range

I need to do a Vlookup of an ID on the source sheet to a table in the data sheet. When the Vlookup is done, it needs to return the cell values from 6 different columns.
Here I have a function to get the range:
Function find_Col(header As String) As Range
Dim aCell As Range, rng As Range, def_Header As Range
Dim col As Long, lRow As Long, defCol As Long
Dim colName As String, defColName As String
Dim y As Workbook
Dim ws1 As Worksheet
Set y = Workbooks("Template.xlsm")
Set ws1 = y.Sheets("Results")
With ws1
Set def_Header = Cells.Find(what:="ID", LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
Set aCell = .Range("B2:Z2").Find(what:=header, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
defCol = def_Header.Column
defColName = Split(.Cells(, defCol).Address, "$")(1)
col = aCell.Column
colName = Split(.Cells(, col).Address, "$")(1)
lRow = Range(defColName & .Rows.count).End(xlUp).Row - 1
Set myCol = Range(colName & "2")
'This is your range
Set find_Col = Range(myCol.Address & ":" & colName & lRow).Offset(1, 0)
'If not found
Else
MsgBox "Column Not Found"
End If
End With
End Function
Then in my sub, I select the range and do a Vlookup which fills this range:
Selection.FormulaR1C1 = "=VLOOKUP(RC[-4],myTable,2,FALSE)"
And this works great.
Then I needed to return more than just one column, so I ended up with the formula:
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Source Sheet:
Data Sheet:
So, my function returns only the range for one column, which I think I can use in terms of getting a row count then using something like this:
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Selection.FormulaArray = "=VLOOKUP($C3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow
Then perhaps instead of C3 it could look something like this:
C & currentRow --> Selection.FormulaArray = "=VLOOKUP($C & currentRow,myTable,{2,3,4,5,6},FALSE)"
But then I have the issue that only one cell is selected (G3) and from H-L is not. And I have no idea whether this is even a plausible effort.
Ideally of course, I would have cells G3:L3 selected and fill the formula down to the last row.
My brain is just fried from all the thinking and attempts.
So this should do the trick... I've explained every instance but if you need help understanding just ask:
Option Explicit
Sub FillData1()
Dim ws As Worksheet, wsData As Worksheet, arr As Variant, arrData As Variant
Dim DictHeaders As Scripting.Dictionary, DictIds As Scripting.Dictionary, DictDataHeaders As Scripting.Dictionary, _
DictDataIds As Scripting.Dictionary
Dim LastRow As Long, LastCol As Integer, i As Long, j As Integer
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
With ThisWorkbook
Set ws = .Sheets("Results")
Set wsData = .Sheets("List")
End With
'Lets suppose your data always starts on row 2 in both sheets and column B will always have the max amount of rows filled
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arr = .Range("B2", .Cells(LastRow, LastCol)).Value
End With
With wsData 'filling the data array
LastRow = .Cells(.Rows.Count, 4).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
arrData = .Range("A2", .Cells(LastRow, LastCol)).Value
End With
'Now lets put everything into Dictionaries so if the data moves columns or rows won't matter
Set DictHeaders = New Scripting.Dictionary
Set DictIds = New Scripting.Dictionary
For i = 1 To UBound(arr, 2) 'this will fill the headers positions on the main sheet
If Not DictHeaders.Exists(arr(1, i)) Then DictHeaders.Add arr(1, i), i
Next i
For i = 2 To UBound(arr, 1) 'this will fill the IDs positions on the main sheet
If Not DictIds.Exists(arr(i, DictHeaders("KW ID"))) Then DictIds.Add arr(i, 1), i
Next i
Set DictDataHeaders = New Scripting.Dictionary
Set DictDataIds = New Scripting.Dictionary
For i = 1 To UBound(arrData, 2) 'this will fill the headers positions on the data sheet
If Not DictDataHeaders.Exists(arrData(1, i)) Then DictDataHeaders.Add arrData(1, i), i
Next i
For i = 2 To UBound(arrData, 1) 'this will fill the IDs positions on the data sheet
If Not DictDataIds.Exists(arrData(i, DictDataHeaders("KW ID"))) Then DictDataIds.Add arrData(i, DictDataHeaders("KW ID")), i
Next i
'Finally will loop through the main array to fill it with the data from the data array
On Error Resume Next
For i = 2 To UBound(arr)
For j = 6 To UBound(arr, 2) 'I'm assuming you want to avoid the first columns which are hidden
arr(i, j) = arrData(DictDataIds(arr(i, 1)), DictDataHeaders(arr(1, j)))
Next j
Next i
On Error GoTo 0
With ws 'filling the first array
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
LastCol = .Cells(2, .Columns.Count).End(xlToLeft).Column
.Range("B2", .Cells(LastRow, LastCol)).Value = arr
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
I don't know if I got the true issue of your goal. However, since your Selection parts in your code should be avoid, why don't make something like the following?
Set myRng = find_Col("Product")
For currentRow = myRng.Rows.count To 1 Step -1
Range(Cells(currentRow, 5), Cells(currentRow, 9)).FormulaArray = "=VLOOKUP(RC3,myTable,{2,3,4,5,6},FALSE)"
Next currentRow

Delete specifics rows in a sheet with more than 60000 rows

I need to delete the rows that:
- Does not have the Word "Get" into Column A, example: if A1 is Configuration Get, I should not delete; but if it is nFormat or anything else, I should delete.
- And for the rows which has the word "get" I also need to check if in Column C the value is 0, if it is not 0 I also should delete.
My function is working for sheet with a small number of rows, but the problem is, I really need to run it for a large number, let's say for 60000 rows. Could someone help me?
My function is:
Sub DeleteRows()
Dim c As Range
Dim ColumnA
Dim Lrow As Long
Dim Lastrow As Long
With Sheets("Sheet1") 'I'm using the Sheet1
.Select
Set ColumnA = ActiveSheet.UsedRange
Lastrow = .UsedRange.Rows(.UsedRange.Rows.Count).Row
For Lrow = Lastrow To 1 Step -1
Set ColumnA = Cells(Lrow, "A") 'I'm looking just in Column "A" for a Get
Set c = ColumnA.Find("Get", LookIn:=xlValues)
If Not c Is Nothing Then
' If the cell has a Get, it will look for a 0 in Column "C"
With .Cells(Lrow, "C")
If Not IsError(.Value) Then
' If the Value is not 0 the row will be delete.
If Not (.Value = 0) Then .EntireRow.Delete
End If
End With
Else
'If didn't find a "Get", it will delete the row
ColumnA.EntireRow.Delete
End If
Next Lrow
End With
End Sub
Try something like this which uses AutoFilter instead
It is the VBA equivalent of:
finding the first blank column
entering =OR(ISERROR(FIND("Get",$A1)),AND(NOT(ISERROR(FIND("Get",$A1))),$C1<>0)) in row 1 and copying down
deleting and TRUE results
cleaning up the working column
code
Sub KillEm()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Set rng1 = Cells.Find("*", , xlValues, , xlByColumns, xlPrevious)
Set rng2 = Cells.Find("*", , xlValues, , xlByRows, xlPrevious)
Set rng3 = Range(Cells(rng2.Row, rng1.Column), Cells(1, rng1.Column))
Application.ScreenUpdating = False
With rng3.Offset(0, 1)
.FormulaR1C1 = "=OR(ISERROR(FIND(""Get"",RC1)),AND(NOT(ISERROR(FIND(""Get"",RC1))),RC3<>0))"
.AutoFilter Field:=1, Criteria1:="TRUE"
.Offset(1, 0).Resize(rng3.Rows.Count - 1, 1).EntireRow.Delete
.EntireColumn.Delete
End With
Application.ScreenUpdating = True
End Sub
I did like this for, in this case sheet7, and it worked:
Application.ScreenUpdating = False
With Sheet7
r = 1
Do While r <= LastRow
If IsError(.Cells(r, 1)) Then
.Rows(r).Delete
LastRow = LastRow - 1
Else
If InStr(.Cells(r, 1), "Get") = 0 Then
.Rows(r).Delete
LastRow = LastRow - 1
Else
r = r + 1
End If
End If
Loop
End With
Application.ScreenUpdating = True

Resources