Reputation: 3
I am basically try to create a VBA that finds duplicates across the whole workbook, highlights them red, and lists the cell address of the duplicate(s) in an adjacent cell. Not sure if this is possible. I can make it highlight duplicates in red in a single sheet, but not across a workbook. And no idea how to get it to report the cell.address. Basically what the FIND function does, when I Ctrl+F and "Find All" it lists all the duplicates and the cell address of the duplicates. But I need it to be able to check multiple worksheets of hundreds of lines of data, instead of me searching one at a time.
For Example (sorry can't embed a picture atm)
SHEET 1:
A -- B
1 Animal -- Duplicate
2 cat -- SHEET1 A5, SHEET2 A5
3 turtle
4 raccoon -- SHEET2 A4
5 cat -- SHEET1 A2, SHEET2 A5
6 monkey -- SHEET2 A7
7 whale
SHEET2:
A -- B
1 Animal -- Duplicate
2 dog
3 rat
4 raccoon -- SHEET1 A4
5 cat -- SHEET1 A2, SHEET1 A5
6 lizard
7 monkey -- SHEET1 A6
Is something like this possible?
EDIT I was using this initially to highlight in red - but I have more sheets now and this is going to get long. I know I need something like.. "For Each ws In ThisWorkbook.Worksheets" but not sure where and how to change what I have to use it.. I feel like I'd need something totally different and wouldn't be able to use my existing code. And then adding in the cell.address to an adjacent cell alludes me completeley.
Sub Duplicate_Digits()
Dim s1 As Worksheet: Set S1 = ThisWorkbook.Sheets("Sheet1")
Dim s2 As Worksheet: Set S2 = ThisWorkbook.Sheets("Sheet2")
Dim s3 As Worksheet: Set S3 = ThisWorkbook.Sheets("Sheet3")
Dim Numbers1, Numbers2, Numbers3, i
Dim Found As Range
Dim ws As Worksheet
Numbers1 = s1.Range("A2:A" & s1.Range("A" &
s1.Rows.Count).End(xlUp).Row).Value
Numbers2 = s2.Range("A2:A" & s2.Range("A" &
s2.Rows.Count).End(xlUp).Row).Value
Numbers3 = s3.Range("A2:A" & s3.Range("A" &
s3.Rows.Count).End(xlUp).Row).Value
For i = LBound(Numbers2, 1) To UBound(Numbers2, 1)
Set Found = s1.Range("A:A").Find(Numbers2(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
For i = LBound(Numbers3, 1) To UBound(Numbers3, 1)
Set Found = s1.Range("A:A").Find(Numbers3(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
For i = LBound(Numbers1, 1) To UBound(Numbers1, 1)
Set Found = s2.Range("A:A").Find(Numbers1(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
For i = LBound(Numbers3, 1) To UBound(Numbers3, 1)
Set Found = s2.Range("A:A").Find(Numbers3(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
For i = LBound(Numbers1, 1) To UBound(Numbers1, 1)
Set Found = s3.Range("A:A").Find(Numbers1(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
For i = LBound(Numbers2, 1) To UBound(Numbers2, 1)
Set Found = s3.Range("A:A").Find(Numbers2(i, 1))
If Not Found Is Nothing Then
Found.Interior.Color = vbRed
End If
Set Found = Nothing
Next i
End Sub
EDIT
Upvotes: 0
Views: 160
Reputation: 11755
First, build a dictionary of all the values in column B of all the sheets and then go back thru the sheets and highlight the duplicates...
Sub FindDups()
Dim sh As Worksheet
Dim lRow As Long
Dim lLastRow As Long
Dim sText As String
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
' count unique values over all worksheets
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
sText = "" & sh.Range("B" & lRow).Value2
If dict.Exists(sText) Then
dict.Item(sText) = dict.Item(sText) + 1
Else
dict.Add sText, 1
End If
Next
Next
' go back thru all the sheets and highlight the cells that have a count greater than 1
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
If dict.Item("" & sh.Range("B" & lRow).Value2) > 1 Then
sh.Range("B" & lRow).Interior.Color = vbRed
Debug.Print sh.Range("B" & lRow).Value2 & " - " & sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "") & " - Count:" & dict.Item("" & sh.Range("B" & lRow).Value2)
' add the results to column C (?)
sh.Range("C" & lRow).Value2 = sh.Range("B" & lRow).Value2 & " - " & sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "") & " - Count:" & dict.Item("" & sh.Range("B" & lRow).Value2)
Else
' add column C info for unique values found
sh.Range("C" & lRow).Value2 = "Unique"
End If
Next
Next
End Sub
There may be an easier way, but this should do what you want if I understand you correctly.
It looks like you are then also trying to create a summary which I did not include code for, but you can modify the Debug.Print
part to build up a summary to add to a new sheet if that's what you are trying to do.
That should at least get you started in the right direction.
Update
Here is how to process the results using another dictionary. It still doesnt do EXACTLY what you want, but I cant be expected to write it ALL for you, right? You can use this example to get what you want:
Sub FindDups()
Dim sh As Worksheet
Dim lRow As Long
Dim lLastRow As Long
Dim sText As String
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
' count unique values over all worksheets
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
sText = "" & sh.Range("B" & lRow).Value2
If dict.Exists(sText) Then
dict.Item(sText) = dict.Item(sText) + 1
Else
dict.Add sText, 1
End If
Next
Next
Dim sKey As Variant
Dim sReport As String
Dim dict2 As Object
Set dict2 = CreateObject("Scripting.Dictionary")
' go back thru all the sheets and highlight the cells that have a count greater than 1
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
sKey = "" & sh.Range("B" & lRow).Value2
If dict.Item(sKey) > 1 Then
sh.Range("B" & lRow).Interior.Color = vbRed
'Debug.Print sKey & " - " & sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "") & " - Count:" & dict.Item("" & sh.Range("B" & lRow).Value2)
' add the results to column C (?)
'sh.Range("C" & lRow).Value2 = sReport
' add the values to a new dictionary object
sReport = sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "")
If Not dict2.Exists(sKey) Then
dict2.Add sKey, sReport & ", "
Else
dict2(sKey) = dict2(sKey) & sReport & ", "
End If
Else
' add column C info for unique values found
sh.Range("C" & lRow).Value2 = "Unique"
End If
Next
Next
' send the report to the debug window
' you can process this info however you want
For Each sKey In dict2.Keys
sReport = sKey & " - " & dict2.Item(sKey)
Debug.Print Left$(sReport, Len(sReport) - 2)
Next
End Sub
3rd Update
OK, I was bored, so I added the last step, and combined step 1 and step 2 into one step. I came up with this, which isn't as easy to read, but it's less code, faster, and gives you all of what you wanted. Simple with dictionaries, eh?
Sub FindDups()
Dim sh As Worksheet
Dim lRow As Long
Dim lLastRow As Long
Dim sKey As String
Dim sReport As String
Dim dictCount As Object
Set dictCount = CreateObject("Scripting.Dictionary")
Dim dictReport As Object
Set dictReport = CreateObject("Scripting.Dictionary")
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
sKey = "" & sh.Range("B" & lRow).Value2
sReport = sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "")
If Not dictCount.Exists(sKey) Then
dictCount.Add sKey, 1
dictReport.Add sKey, sReport & ", "
Else
dictCount.Item(sKey) = dictCount.Item(sKey) + 1
dictReport(sKey) = dictReport(sKey) & sReport & ", "
End If
Next
Next
Dim sNewText As String
For Each sh In ActiveWorkbook.Worksheets
lLastRow = sh.Range("B" & sh.Rows.Count).End(xlUp).Row
For lRow = 1 To lLastRow
sKey = "" & sh.Range("B" & lRow).Value2
If dictCount.Item(sKey) > 1 Then
sh.Range("B" & lRow).Interior.Color = vbRed
sNewText = Replace(dictReport(sKey), sh.Name & "!" & Replace(sh.Range("B" & lRow).Address, "$", "") & ", ", "")
sh.Range("C" & lRow).Value = Left$(sNewText, Len(sNewText) - 2)
Else
sh.Range("C" & lRow).Value2 = "Unique"
End If
Next
Next
End Sub
Upvotes: 1