Macro adjustment - compare two sheets

Robert Mika

MrExcel MVP
Joined
Jun 29, 2009
Messages
7,256
I have report downloading every day.
It has around 500 rows.
Every day I need to go through all of the lines to find small changes.

This macro is comparing two rows in sheets and will copy all differences to Sheet3.

Code:
Public Sub ReconReport()
    Dim rngCell As Range
    
    For Each rngCell In Worksheets("Sheet1").UsedRange
        If Not rngCell = Worksheets("Sheet2").Cells(rngCell.Row, rngCell.Column) Then _
            Let Worksheets("Sheet3").Cells(rngCell.Row, rngCell.Column) = rngCell
    Next
End Sub
The problem is that it is comparing rows not actually entries.
(Data in row of today's report (let say 2) can be in different row on yesterday’s (let say 5)
Is the a way to compare those entries and paste the difference to another sheet?
 

Excel Facts

Show numbers in thousands?
Use a custom number format of #,##0,K. Each comma after the final 0 will divide the displayed number by another thousand
Hi guys,

This code saved me hours of work. Thank you very much! I just wanted to make a few alterations to the exceptions page. Instead of showing the entire row witht eh differences hiloghted, I wanted to present it in the following way:


Type ID Data Label Sheet1 (Before) Sheet2 (After)
Changed 4 Name Smith, John Smith, S. John

Used the following version of the code:

Option Explicit
Dim miMaxColumns As Integer
Sub CompareSheets()
Dim bChanged As Boolean, baChanged() As Boolean
Dim iColEnd As Integer, iCol As Integer, iCol1 As Integer, iCol2 As Integer
Dim lRow1 As Long, lRow2 As Long, lReportRow As Long
Dim objDictOld As Object, objDictNew As Object
Dim vKeys As Variant, vKey As Variant
Dim vaInput() As Variant, vaOutput() As Variant, vaOutput2() As Variant
Dim vaInputOld As Variant, vaInputNew As Variant
Dim wsOld As Worksheet, wsNew As Worksheet, wsReport As Worksheet

Set wsOld = Sheets("KPMG")
miMaxColumns = wsOld.Cells(1, Columns.Count).End(xlToLeft).Column
Set objDictOld = PopulateDictionary(WS:=wsOld)
Set wsNew = Sheets("Client")
Set objDictNew = PopulateDictionary(WS:=wsNew)
Set wsReport = Sheets("ExceptionSummary")
With wsReport
.Cells.ClearFormats
.Cells.ClearContents
End With
wsOld.Range("A1:" & wsOld.Cells(1, miMaxColumns).Address).Copy
wsReport.Range("B1").PasteSpecial xlPasteValues
Application.CutCopyMode = False
lReportRow = 1
vKeys = objDictOld.Keys
For Each vKey In vKeys
ReDim vaInputOld(1 To 1, 1 To miMaxColumns)
vaInputOld = objDictOld.Item(vKey)
If objDictNew.exists(vKey) Then
ReDim vaInputNew(1 To 1, 1 To miMaxColumns)
vaInputNew = objDictNew.Item(vKey)
ReDim vaOutput(1 To 1, 1 To miMaxColumns + 1)
ReDim vaOutput2(1 To 1, 1 To miMaxColumns + 1)
ReDim baChanged(1 To miMaxColumns)
bChanged = False
For iCol = 1 To miMaxColumns
vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
If vaInputOld(1, iCol) <> vaInputNew(1, iCol) Then
vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
baChanged(iCol) = True
bChanged = True
End If
Next iCol
If bChanged Then
lReportRow = lReportRow + 1
For iCol = 1 To UBound(baChanged)
If baChanged(iCol) Then
With wsReport
.Range(.Cells(lReportRow, iCol + 1).Address, _
.Cells(lReportRow + 1, iCol + 1).Address).Interior.Color = vbYellow
End With
End If
Next iCol

vaOutput(1, 1) = "Changed"
With wsReport
.Range(.Cells(lReportRow, 1).Address, _
.Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput
lReportRow = lReportRow + 1
.Range(.Cells(lReportRow, 1).Address, _
.Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput2
End With
End If
objDictOld.Remove vKey
objDictNew.Remove vKey
Else
ReDim vaOutput(1 To 1, 1 To miMaxColumns + 1)
vaOutput(1, 1) = "Deleted"
For iCol = 1 To miMaxColumns
vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
Next iCol

lReportRow = lReportRow + 1
With wsReport
.Range(.Cells(lReportRow, 1).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput
'-- Set the row to light grey
.Range(.Cells(lReportRow, 2).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Interior.ColorIndex = 15
End With
End If
Next vKey
If objDictNew.Count <> 0 Then
vKeys = objDictNew.Keys
For Each vKey In vKeys
ReDim vaOutput2(1 To 1, 1 To miMaxColumns + 1)
vaInputNew = objDictNew.Item(vKey)
vaOutput2(1, 1) = "Inserted"
For iCol = 1 To miMaxColumns
vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
Next iCol
lReportRow = lReportRow + 1
With wsReport
.Range(.Cells(lReportRow, 1).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput2
'-- Set the row to light green
.Range(.Cells(lReportRow, 2).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Interior.ColorIndex = 4
End With
Next vKey
End If
objDictOld.RemoveAll
Set objDictOld = Nothing
objDictNew.RemoveAll
Set objDictNew = Nothing
End Sub
Private Function PopulateDictionary(ByRef WS As Worksheet) As Object
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String
Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, "A").End(xlUp).Row
For lRow = 2 To lRowEnd
sKey = Trim$(LCase$(CStr(WS.Range("A" & lRow).Value)))
On Error Resume Next
PopulateDictionary.Add Key:=sKey, Item:=WS.Range(WS.Cells(lRow, 1).Address, _
WS.Cells(lRow, miMaxColumns).Address).Value
On Error GoTo 0
Next lRow
End Function


Thanks
 
Upvote 0
Hi,

I see that the macro has been amended to suit your requirements, can you re-post by enclosing the macro code in code tags?
To do this, reply, then select "Go Advanced" then click the # button and paste the code in the code tag envelope

Can you confirm that your key is in column A?
 
Upvote 0
Hi Alan,


Apologies for the late reply. Yes the code has been amended somewhat. For the deleted or inserted items, it would simply state for example:
Type of Change - Field - Sheet 1 - Sheet 2
Inserted. ID. (blank) 109
Inserted. Name (blank) Doe, John
Deleted. Address NYC. (blank)


The (blanks) are actually blank cells bytheway. And yes, the key in Sheet1 is located in column A.


Thanks.
 
Upvote 0
Hi Alan,


Apologies for the late reply. Yes the code has been amended somewhat. For the deleted or inserted items, it would simply state for example:
Type of Change - Field - Sheet 1 - Sheet 2
Inserted. ID. (blank) 109
Inserted. Name (blank) Doe, John
Deleted. Address NYC. (blank)


The (blanks) are actually blank cells bytheway. And yes, the key in Sheet1 is located in column A.


Thanks.



P.S. Essentially, the info in the exceptions sheet will be transposed in comparison to what we have now.
 
Upvote 0
Hi,

Try this macro:
Code:
Option Explicit
Const msOldSheetname As String = "KPMG"
Const msNewSheetName As String = "Client"
Const msReportSheetName As String = "ExceptionSummary"

Const msActionChanged As String = "Changed"
Const msActtionInserted As String = "Inserted"
Const msActionDeleted As String = "Deleted"

Dim miMaxColumns As Integer
Sub CompareSheets()
Dim bChanged As Boolean, baChanged() As Boolean
Dim iColEnd As Integer, iCol As Integer, iCol1 As Integer, iCol2 As Integer
Dim lRow1 As Long, lRow2 As Long, lReportRow As Long
Dim objDictOld As Object, objDictNew As Object
Dim sChangeType As String
Dim sCurKey As String
Dim vKeys As Variant, vKey As Variant
Dim vaInput As Variant, vaOutput As Variant
Dim vaHeadings As Variant
Dim vaHeadingsNew As Variant
Dim vaInputOld As Variant, vaInputNew As Variant
Dim wsOld As Worksheet, wsNew As Worksheet, wsReport As Worksheet

Set wsOld = Sheets(msOldSheetname)
miMaxColumns = wsOld.Cells(1, Columns.Count).End(xlToLeft).Column
Set objDictOld = PopulateDictionary(WS:=wsOld)
Set wsNew = Sheets(msNewSheetName)
Set objDictNew = PopulateDictionary(WS:=wsNew)

vaHeadings = wsOld.Range("A1:" & wsOld.Cells(1, miMaxColumns).Address).Value
vaHeadingsNew = wsNew.Range("A1:" & wsNew.Cells(1, miMaxColumns).Address).Value
For iCol = 1 To UBound(vaHeadingsNew, 2)
    If LCase$(CStr(vaHeadingsNew(1, iCol))) <> LCase$(CStr(vaHeadings(1, iCol))) Then
        vaHeadings(1, iCol) = vaHeadings(1, iCol) & " (" & vaHeadingsNew(1, iCol) & ")"
    End If
Next iCol

Set wsReport = Sheets(msReportSheetName)

With wsReport
    .Cells.ClearContents
End With

lReportRow = 1
ReDim vaOutput(1 To 1, 1 To 5)
vaOutput = Array("Type", "Key", "Field", wsOld.Name, wsNew.Name)
wsReport.Range("A1:E1").Value = vaOutput

vKeys = objDictOld.Keys
For Each vKey In vKeys
    ReDim vaInputOld(1 To 1, 1 To miMaxColumns)
    vaInputOld = objDictOld.Item(vKey)
    ReDim vaOutput(1 To 1, 1 To 5)
    vaOutput(1, 2) = vKey
    ReDim vaInputNew(1 To 1, 1 To miMaxColumns)
    If objDictNew.exists(vKey) Then
        sChangeType = msActionChanged
        vaInputNew = objDictNew.Item(vKey)
    Else
        sChangeType = msActionDeleted
    End If
    
    ReportChanges ChangeType:=sChangeType, _
                    Key:=vKey, _
                    OldData:=vaInputOld, _
                    NewData:=vaInputNew, _
                    Headings:=vaHeadings, _
                    ReportRow:=lReportRow, _
                    wsReport:=wsReport
    
    On Error Resume Next
    objDictOld.Remove vKey
    objDictNew.Remove vKey
    On Error GoTo 0
    
Next vKey

If objDictNew.Count <> 0 Then
    
    vKeys = objDictNew.Keys
    ReDim vaInputOld(1 To 1, 1 To miMaxColumns)
    For Each vKey In vKeys
        vaInputNew = objDictNew.Item(vKey)
        ReportChanges ChangeType:=msActtionInserted, _
                        Key:=vKey, _
                        OldData:=vaInputOld, _
                        NewData:=vaInputNew, _
                        Headings:=vaHeadings, _
                        ReportRow:=lReportRow, _
                        wsReport:=wsReport
    Next vKey
End If

objDictOld.RemoveAll
Set objDictOld = Nothing
objDictNew.RemoveAll
Set objDictNew = Nothing
End Sub

Private Sub ReportChanges(ByVal ChangeType As String, _
                          ByVal Key As Variant, _
                          ByVal OldData As Variant, _
                          ByRef NewData As Variant, _
                          ByRef Headings As Variant, _
                          ByRef ReportRow As Long, _
                          ByRef wsReport As Worksheet)
Dim baChanged() As Boolean
Dim bChanged As Boolean
Dim iCol As Integer
Dim vaOutput As Variant

ReDim vaOutput(1 To 1, 1 To 5)
vaOutput(1, 1) = ChangeType
vaOutput(1, 2) = Key

ReDim baChanged(1 To UBound(OldData, 2))
bChanged = False
For iCol = 1 To UBound(baChanged)
    If OldData(1, iCol) <> NewData(1, iCol) Then
        vaOutput(1, 3) = Headings(1, iCol)
        vaOutput(1, 4) = OldData(1, iCol)
        vaOutput(1, 5) = NewData(1, iCol)
        ReportRow = ReportRow + 1
        wsReport.Range("A" & ReportRow).Resize(, UBound(vaOutput, 2)).Value = vaOutput
    End If
Next iCol

End Sub
Private Function PopulateDictionary(ByRef WS As Worksheet) As Object
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String

Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, "A").End(xlUp).Row
For lRow = 2 To lRowEnd
    sKey = Trim$(LCase$(CStr(WS.Range("A" & lRow).Value)))
    On Error Resume Next
    PopulateDictionary.Add Key:=sKey, Item:=WS.Range(WS.Cells(lRow, 1).Address, _
                                            WS.Cells(lRow, miMaxColumns).Address).Value
    On Error GoTo 0
Next lRow
End Function
 
Upvote 0
Alan. So I have basically been data testing some of the numbers. A few exceptions are showing up even though they match. Wanted to know if you can take a look at a sample file and let me know what the issue is. Also, wanted to make a few changes to the macro for the following:

1. Is it possible to have a maco run inspite of have a #REF or #VALUE error it in the data set. Thus, what I mean to say is, say Sheet1 had a #REF error, but Sheet2 had a value, can the macro be adjusted so that the exception shows up (basically ignoring the error)?

2. Also, another key I would like to define would be the headers. Therefore, in addition to the ID number in column A of the various rows, I also want to match the headers before goign on to extract data. Therefore, any header that is not included in both Sheet1 and Sheet2 will be avoided.

Thanks a lot for all the help though.
 
Upvote 0
Hi,

On further testing, it seems to not work if you're not on Sheet1 when calling the macro.

Try this amended version:
Code:
Option Explicit
Dim miMaxColumns As Integer
Sub CompareSheets()
Dim bChanged As Boolean, baChanged() As Boolean
Dim iColEnd As Integer, iCol As Integer, iCol1 As Integer, iCol2 As Integer
Dim lRow1 As Long, lRow2 As Long, lReportRow As Long
Dim objDictOld As Object, objDictNew As Object
Dim vKeys As Variant, vKey As Variant
Dim vaInput() As Variant, vaOutput() As Variant, vaOutput2() As Variant
Dim vaInputOld As Variant, vaInputNew As Variant
Dim wsOld As Worksheet, wsNew As Worksheet, wsReport As Worksheet


Set wsOld = Sheets("Sheet1")
miMaxColumns = wsOld.Cells(1, Columns.Count).End(xlToLeft).Column
Set objDictOld = PopulateDictionary(WS:=wsOld)
Set wsNew = Sheets("Sheet2")
Set objDictNew = PopulateDictionary(WS:=wsNew)

Set wsReport = Sheets("Sheet3")

With wsReport
    .Cells.ClearFormats
    .Cells.ClearContents
End With

wsOld.Range("A1:" & wsOld.Cells(1, miMaxColumns).Address).Copy
wsReport.Range("B1").PasteSpecial xlPasteValues
Application.CutCopyMode = False

lReportRow = 1
vKeys = objDictOld.Keys
For Each vKey In vKeys
    ReDim vaInputOld(1 To 1, 1 To miMaxColumns)
    vaInputOld = objDictOld.Item(vKey)
    If objDictNew.exists(vKey) Then
        ReDim vaInputNew(1 To 1, 1 To miMaxColumns)
        vaInputNew = objDictNew.Item(vKey)
        ReDim vaOutput(1 To 1, 1 To miMaxColumns + 1)
        ReDim vaOutput2(1 To 1, 1 To miMaxColumns + 1)
        ReDim baChanged(1 To miMaxColumns)
        bChanged = False
        For iCol = 1 To miMaxColumns
            vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
            If vaInputOld(1, iCol) <> vaInputNew(1, iCol) Then
                vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
                baChanged(iCol) = True
                bChanged = True
            End If
        Next iCol
        If bChanged Then
            lReportRow = lReportRow + 1
            For iCol = 1 To UBound(baChanged)
                If baChanged(iCol) Then
                    With wsReport
                        .Range(.Cells(lReportRow, iCol + 1).Address, _
                               .Cells(lReportRow + 1, iCol + 1).Address).Interior.Color = vbYellow
                    End With
                End If
            Next iCol
            
            vaOutput(1, 1) = "Changed"
            With wsReport
                .Range(.Cells(lReportRow, 1).Address, _
                       .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput
                lReportRow = lReportRow + 1
                .Range(.Cells(lReportRow, 1).Address, _
                       .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput2
            End With
        End If
        objDictOld.Remove vKey
        objDictNew.Remove vKey
    Else
        ReDim vaOutput(1 To 1, 1 To miMaxColumns + 1)
        vaOutput(1, 1) = "Deleted"
        For iCol = 1 To miMaxColumns
            vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
        Next iCol
        
        lReportRow = lReportRow + 1
        With wsReport
            .Range(.Cells(lReportRow, 1).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput
            '-- Set the row to light grey
            .Range(.Cells(lReportRow, 2).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Interior.ColorIndex = 15
        End With
    End If
Next vKey

If objDictNew.Count <> 0 Then
    vKeys = objDictNew.Keys
    For Each vKey In vKeys
        ReDim vaOutput2(1 To 1, 1 To miMaxColumns + 1)
        vaInputNew = objDictNew.Item(vKey)
        vaOutput2(1, 1) = "Inserted"
        For iCol = 1 To miMaxColumns
            vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
        Next iCol
        lReportRow = lReportRow + 1
        With wsReport
            .Range(.Cells(lReportRow, 1).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Value = vaOutput2
            '-- Set the row to light green
            .Range(.Cells(lReportRow, 2).Address, .Cells(lReportRow, miMaxColumns + 1).Address).Interior.ColorIndex = 4
        End With
    Next vKey
End If

objDictOld.RemoveAll
Set objDictOld = Nothing
objDictNew.RemoveAll
Set objDictNew = Nothing
End Sub
Private Function PopulateDictionary(ByRef WS As Worksheet) As Object
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String

Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, "A").End(xlUp).Row
For lRow = 2 To lRowEnd
    sKey = Trim$(LCase$(CStr(WS.Range("A" & lRow).Value)))
    On Error Resume Next
    PopulateDictionary.Add key:=sKey, Item:=WS.Range(WS.Cells(lRow, 1).Address, _
                                            WS.Cells(lRow, miMaxColumns).Address).Value
    On Error GoTo 0
Next lRow
End Function


Dear al_b_cnu. I have been using your Macro for a long time. It worked perfect. I really appreciate that.
However, I have sheets that 2 keys or 3 keys and different attributes. Below is an example of sheet1.
Table 1 has column A and B as a key. Table 2 has column A,B,&C as a key.

I tried to modified the code to add 2 keys to compare. But it failed.
Please help! Many thanks!!!



CityTown
Sequence

<tbody>
</tbody>
Display Value

<tbody>
</tbody>
DataValue

<tbody>
</tbody>
Status

<tbody>
</tbody>
CityTown

<tbody>
</tbody>
1
Aberdeen

<tbody>
</tbody>
Aberdeen

<tbody>
</tbody>
Added

<tbody>
</tbody>
CityTown

<tbody>
</tbody>
2
American Falls

<tbody>
</tbody>
American Falls

<tbody>
</tbody>
Added

<tbody>
</tbody>
CityTown

<tbody>
</tbody>
3
Ammon

<tbody>
</tbody>
Ammon

<tbody>
</tbody>
Added

<tbody>
</tbody>
CityTown

<tbody>
</tbody>
4
Arco

<tbody>
</tbody>
Arco

<tbody>
</tbody>
Added

<tbody>
</tbody>
Liability

<tbody>
</tbody>
CoverageType

<tbody>
</tbody>
Sequence

<tbody>
</tbody>
Display Value

<tbody>
</tbody>
DataValue

<tbody>
</tbody>
Status

<tbody>
</tbody>
LiabilityLimitText

<tbody>
</tbody>
LiabilityLimitText

<tbody>
</tbody>
1
Not Applicable

<tbody>
</tbody>
Not Applicable
Added

<tbody>
</tbody>
LiabilityLimitText

<tbody>
</tbody>
Combined Single Limit

<tbody>
</tbody>
2
10,000

<tbody>
</tbody>
10,000
Deleted

<tbody>
</tbody>
LiabilityLimitText

<tbody>
</tbody>
Combined Single Limit

<tbody>
</tbody>
3
65,000

<tbody>
</tbody>
65,000

<tbody>
</tbody>
Added

<tbody>
</tbody>

<tbody>
</tbody>
 
Upvote 0

Forum statistics

Threads
1,215,066
Messages
6,122,948
Members
449,095
Latest member
nmaske

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top