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

Format cells as date
Select range and press Ctrl+Shift+3 to format cells as date. (Shift 3 is the # sign which sort of looks like a small calendar).
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

Thanks for the code 'al_b_cnu'! This is GREAT!

Does this only work with 5 columns of data (I tried 1 column and it did not work)? What if I want 9 consecutive columns or just 1 column to compare with sheets? What parameters need to be changed as you change the number of columns with data in it?

Also, how would you change things if instead of comparing 2 sheets in the same workbook and writing results to a 3rd sheet, instead you use sheet 1 of three different workbooks (2 workbooks for the comparisons and a different workbook for the results)?

Thanks!
 
Upvote 0
Hi,

Try this macro:
Code:
Option Explicit

Dim miMaxColumns As Integer
Dim miaHeadingCols1() As Integer
Dim miaHeadingCols2() As Integer
Dim msaCompareWorkbooks() As String
Dim msResultsSheet As String
Dim msaHeadings1() As String
Dim msaHeadings2() As String
Dim mbaHeadingsInfo() As Boolean
Dim mwbInputs() As Workbook
Dim mwbReport As Workbook
Dim mwsInputs() As Worksheet
Dim mwsReport As Worksheet

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 vaHeadings() As Variant
Dim wsOld As Worksheet, wsNew As Worksheet

If GetParameters = False Then Exit Sub

Set wsOld = mwsInputs(1)
Set wsNew = mwsInputs(2)

If PopulateHeadingColumns(WS:=wsOld, HeadingsTexts:=msaHeadings1, HeadingsColumns:=miaHeadingCols1) = False Then
    Exit Sub
End If

If PopulateHeadingColumns(WS:=wsNew, HeadingsTexts:=msaHeadings2, HeadingsColumns:=miaHeadingCols2) = False Then
    Exit Sub
End If

miMaxColumns = UBound(msaHeadings1) + 1
Set objDictOld = PopulateDictionary(WS:=wsOld, ColumnPositions:=miaHeadingCols1)
Set objDictNew = PopulateDictionary(WS:=wsNew, ColumnPositions:=miaHeadingCols2)

With mwsReport
    .Cells.ClearFormats
    .Cells.ClearContents
End With

ReDim vaHeadings(1 To 1, 1 To UBound(msaHeadings1) + 2)
For iCol = 0 To UBound(msaHeadings1)
    If msaHeadings1(iCol) = msaHeadings2(iCol) Then
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol)
    Else
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol) & " / " & msaHeadings2(iCol)
    End If
Next iCol
mwsReport.Range("A1", mwsReport.Cells(1, UBound(vaHeadings, 2)).Address).Value = vaHeadings

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)
                If mbaHeadingsInfo(iCol - 1) = False Then
                    baChanged(iCol) = True
                    bChanged = True
                End If
            End If
        Next iCol
        If bChanged Then
            lReportRow = lReportRow + 1
            For iCol = 1 To UBound(baChanged)
                If baChanged(iCol) Then
                    With mwsReport
                        .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 mwsReport
                .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) = wsOld.Name & " only"
        For iCol = 1 To miMaxColumns
            vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
        Next iCol
        
        lReportRow = lReportRow + 1
        With mwsReport
            .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) = wsNew.Name & " only"
        For iCol = 1 To miMaxColumns
            vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
        Next iCol
        lReportRow = lReportRow + 1
        With mwsReport
            .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

With Application
    .EnableEvents = False
    .DisplayAlerts = False
End With
mwbInputs(1).Close savechanges:=False
mwbInputs(2).Close savechanges:=False
Application.EnableEvents = True
End Sub
Private Function PopulateDictionary(ByRef WS As Worksheet, ByRef ColumnPositions() As Integer) As Object
Dim iPtr As Integer, iCurCol As Integer
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String
Dim vaItem() As Variant

Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, ColumnPositions(0)).End(xlUp).Row
For lRow = 2 To lRowEnd
    sKey = Trim$(LCase$(CStr(WS.Cells(lRow, ColumnPositions(0)).Value)))
    ReDim vaItem(1 To 1, 1 To UBound(ColumnPositions) + 1)
    For iPtr = 0 To UBound(ColumnPositions)
        iCurCol = ColumnPositions(iPtr)
        vaItem(1, iPtr + 1) = WS.Cells(lRow, iCurCol).Value
    Next iPtr
    On Error Resume Next
    PopulateDictionary.Add key:=sKey, Item:=vaItem
    On Error GoTo 0
Next lRow
End Function

Private Function GetParameters() As Boolean
Dim iPtr As Integer, iPtr1 As Integer
Dim iParamCheck As Integer
Const iParamCompareSheets As Integer = 1
Const iParamResultsSheet As Integer = 2
Const iParamHeadings As Integer = 4

Dim lRow As Long
Dim sCurKey As String
Dim saHeadings() As String, saHeadingsA() As String
Dim vaParameters As Variant
Dim vInputFiles As Variant, vOutputFile As Variant
Dim wsParams As Worksheet, wsTemp As Worksheet


Set wsParams = Nothing
On Error Resume Next
Set wsParams = Sheets("Parameters")
On Error GoTo 0
If wsParams Is Nothing Then
    MsgBox prompt:="Cannot access 'Parameters' sheet", _
            Buttons:=vbOKOnly + vbCritical, _
            Title:="ERROR"
    GetParameters = False
    Exit Function
End If

lRow = wsParams.Cells(Rows.Count, "A").End(xlUp).Row
vaParameters = wsParams.Range("A1:B" & lRow).Value

iParamCheck = 0
For lRow = 2 To UBound(vaParameters, 1)
    sCurKey = Replace(LCase$(CStr(vaParameters(lRow, 1))), " ", "")
    Select Case sCurKey
        
    Case "headings"
        iParamCheck = iParamCheck Or iParamHeadings
        saHeadings = Split(CStr(vaParameters(lRow, 2)), ",")
        
        ReDim msaHeadings1(0 To UBound(saHeadings))
        ReDim msaHeadings2(0 To UBound(saHeadings))
        ReDim miaHeadingCols1(0 To UBound(saHeadings))
        ReDim miaHeadingCols2(0 To UBound(saHeadings))
        ReDim mbaHeadingsInfo(0 To UBound(saHeadings))
        
        For iPtr = 0 To UBound(saHeadings)
            saHeadingsA = Split("=" & saHeadings(iPtr), "=")
            If UBound(saHeadingsA) < 1 Or UBound(saHeadingsA) > 2 Then
                MsgBox prompt:="Invalid headings value", Buttons:=vbOKOnly + vbCritical
                GetParameters = False
                Exit Function
            End If
            ReDim Preserve saHeadingsA(0 To 2)
            saHeadingsA(1) = Trim$(saHeadingsA(1))
            mbaHeadingsInfo(iPtr) = LCase$(Left$(saHeadingsA(1) & "123456", 6)) = "(info)"
            If mbaHeadingsInfo(iPtr) Then saHeadingsA(1) = Mid$(saHeadingsA(1), 7)
            If saHeadingsA(2) = "" Then saHeadingsA(2) = saHeadingsA(1)
            msaHeadings1(iPtr) = saHeadingsA(1)
            msaHeadings2(iPtr) = saHeadingsA(2)
        Next iPtr
    Case Else
        MsgBox prompt:="Unrecognised parameter in row " & lRow, Buttons:=vbOKOnly + vbCritical
        GetParameters = False
        Exit Function
    End Select
Next lRow

vInputFiles = Application.GetOpenFilename(filefilter:="compae workbooks (*.xls*),*.xls*", _
                                     Title:="Please select the two input workbooks", _
                                     MultiSelect:=True)

If IsArray(vInputFiles) Then
    If UBound(vInputFiles) <> 2 Then
        MsgBox prompt:="Please select exactly two input workbooks for comparison", _
                Buttons:=vbOKOnly + vbCritical
        GetParameters = False
        Exit Function
    End If
Else
    MsgBox prompt:="Macro Abandoned", Buttons:=vbOKOnly + vbInformation
    GetParameters = False
    Exit Function
End If

ReDim mwbInputs(1 To 2)
ReDim mwsInputs(1 To 2)
Application.EnableEvents = False
iPtr1 = 2
For iPtr = 1 To 2
    Set mwbInputs(iPtr) = Workbooks.Open(Filename:=vInputFiles(iPtr1), ReadOnly:=True)
    Set mwsInputs(iPtr) = mwbInputs(iPtr).Sheets(1)
    iPtr1 = iPtr1 - 1
Next iPtr

vOutputFile = Application.GetSaveAsFilename(filefilter:="Output Workbook (*.xls*),*.xls*", _
                                            Title:="Please select output workbook or [Cancel]")
If vOutputFile = False Then
    Set mwbReport = Workbooks.Add
Else
    Set mwbReport = Workbooks.Open(Filename:=vOutputFile, ReadOnly:=False)
    Application.EnableEvents = True
End If
Application.EnableEvents = True
Set mwsReport = mwbReport.Sheets(1)

GetParameters = True
End Function

Private Function PopulateHeadingColumns(ByVal WS As Worksheet, _
                                        ByRef HeadingsTexts() As String, _
                                        ByRef HeadingsColumns() As Integer) As Boolean
Dim bFound As Boolean
Dim iPtrCol As Integer, iPtrHeading As Integer, iColEnd As Integer
Dim sCurHeading As String, sCur As String
Dim vaHeadings() As Variant

iColEnd = WS.Cells(1, Columns.Count).End(xlToLeft).Column
vaHeadings = WS.Range("A1:" & WS.Cells(1, iColEnd).Address).Value

For iPtrHeading = LBound(HeadingsTexts) To UBound(HeadingsTexts)
    sCurHeading = Replace(LCase$(HeadingsTexts(iPtrHeading)), " ", "")
    bFound = False
    For iPtrCol = 1 To UBound(vaHeadings, 2)
        If sCurHeading = Replace(LCase$(CStr(vaHeadings(1, iPtrCol))), " ", "") Then
            HeadingsColumns(iPtrHeading) = iPtrCol
            bFound = True
            Exit For
        End If
    Next iPtrCol
    If bFound = False Then
        MsgBox prompt:="Heading '" & HeadingsTexts(iPtrHeading) & "' not found in " & WS.Parent.Name, _
                Buttons:=vbOKOnly + vbCritical
                PopulateHeadingColumns = False
                Exit Function
    End If
Next iPtrHeading

PopulateHeadingColumns = True
End Function

The macro will prompt you for your two input workbooks (press [Ctrl] when selecting the 2nd w/book)
It will then prompt for yhe output w/book, select [Cancel] for the macro to create it.

You will also require a sheet named 'Parameters' to define the headings
For Examnple
<b>Excel 2003</b><table cellpadding="2.5px" rules="all" style=";background-color: #FFFFFF;border: 1px solid;border-collapse: collapse; border-color: #A6AAB6"><colgroup><col width="25px" style="background-color: #E0E0F0" /><col /><col /></colgroup><thead><tr style=" background-color: #E0E0F0;text-align: center;color: #161120"><th></th><th>A</th><th>B</th></tr></thead><tbody><tr ><td style="color: #161120;text-align: center;">1</td><td style=";">Key</td><td style=";">Value</td></tr><tr ><td style="color: #161120;text-align: center;">2</td><td style=";">Headings</td><td style=";">Key, Data2=DataB, Data3=DataC,(info)Description=Desc</td></tr></tbody></table><p style="width:6em;font-weight:bold;margin:0;padding:0.2em 0.6em 0.2em 0.5em;border: 1px solid #A6AAB6;border-top:none;text-align: center;background-color: #E0E0F0;color: #161120">Parameters</p><br /><br />

Input w/book 1:
<b>Excel 2003</b><table cellpadding="2.5px" rules="all" style=";background-color: #FFFFFF;border: 1px solid;border-collapse: collapse; border-color: #A6AAB6"><colgroup><col width="25px" style="background-color: #E0E0F0" /><col /><col /><col /><col /><col /></colgroup><thead><tr style=" background-color: #E0E0F0;text-align: center;color: #161120"><th></th><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th></tr></thead><tbody><tr ><td style="color: #161120;text-align: center;">1</td><td style=";">Key</td><td style=";">Data1</td><td style=";">Data2</td><td style=";">Data 3</td><td style=";">Description</td></tr><tr ><td style="color: #161120;text-align: center;">2</td><td style=";">Alpha</td><td style=";">$B$2</td><td style=";">$C$2</td><td style=";">$D$2</td><td style=";">zzz</td></tr><tr ><td style="color: #161120;text-align: center;">3</td><td style=";">Beta</td><td style=";">$B$3</td><td style=";">$C$3</td><td style=";">$D$3</td><td style=";">xxx</td></tr><tr ><td style="color: #161120;text-align: center;">4</td><td style=";">Gamma</td><td style=";">$B$4</td><td style=";">$C$4</td><td style=";">$D$4</td><td style=";">ccc</td></tr><tr ><td style="color: #161120;text-align: center;">5</td><td style=";">Delta</td><td style=";">$B$5</td><td style=";">$C$5</td><td style=";">$D$5</td><td style=";">vvv</td></tr><tr ><td style="color: #161120;text-align: center;">6</td><td style=";">Epsiln</td><td style=";">$B$6</td><td style=";">$C$6</td><td style=";">$D$6</td><td style=";">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">7</td><td style=";">Zeta</td><td style=";">$B$7</td><td style=";">$C$7</td><td style=";">$D$7</td><td style=";">nnn</td></tr><tr ><td style="color: #161120;text-align: center;">8</td><td style=";">Eta</td><td style=";">$B$8</td><td style=";">$C$8</td><td style=";">$D$8</td><td style=";">mmm</td></tr><tr ><td style="color: #161120;text-align: center;">9</td><td style=";">Theta</td><td style=";">$B$9</td><td style=";">$C$9</td><td style=";">$D$9</td><td style=";">qqq</td></tr></tbody></table><p style="width:3.6em;font-weight:bold;margin:0;padding:0.2em 0.6em 0.2em 0.5em;border: 1px solid #A6AAB6;border-top:none;text-align: center;background-color: #E0E0F0;color: #161120">Sheet1</p><br /><br />

Input W/Book 2:
<b>Excel 2003</b><table cellpadding="2.5px" rules="all" style=";background-color: #FFFFFF;border: 1px solid;border-collapse: collapse; border-color: #A6AAB6"><colgroup><col width="25px" style="background-color: #E0E0F0" /><col /><col /><col /><col /><col /></colgroup><thead><tr style=" background-color: #E0E0F0;text-align: center;color: #161120"><th></th><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th></tr></thead><tbody><tr ><td style="color: #161120;text-align: center;">1</td><td style=";">Key</td><td style=";">DataA</td><td style=";">DataB</td><td style=";">DataC</td><td style=";">Desc</td></tr><tr ><td style="color: #161120;text-align: center;">2</td><td style=";">Alpha</td><td style=";">$B$2</td><td style=";">$C$2</td><td style=";">xxx</td><td style=";">aaa</td></tr><tr ><td style="color: #161120;text-align: center;">3</td><td style=";">Iota</td><td style=";">$B$3</td><td style=";">$C$3</td><td style=";">$D$3</td><td style=";">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">4</td><td style=";">Gamma</td><td style=";">aaa</td><td style=";">$C$4</td><td style=";">$D$4</td><td style=";">ccc</td></tr><tr ><td style="color: #161120;text-align: center;">5</td><td style=";">Kappa</td><td style=";">$B$5</td><td style=";">$C$5</td><td style=";">$D$5</td><td style=";">ddd</td></tr><tr ><td style="color: #161120;text-align: center;">6</td><td style=";">Lambda</td><td style=";">$B$6</td><td style=";">$C$6</td><td style=";">$D$6</td><td style=";">eee</td></tr><tr ><td style="color: #161120;text-align: center;">7</td><td style=";">Zeta</td><td style=";">$B$7</td><td style=";">bbb</td><td style=";">$D$7</td><td style=";">fff</td></tr><tr ><td style="color: #161120;text-align: center;">8</td><td style=";">Eta</td><td style=";">$B$8</td><td style=";">$C$8</td><td style=";">$D$8</td><td style=";">ggg</td></tr><tr ><td style="color: #161120;text-align: center;">9</td><td style=";">Theta</td><td style=";">$B$9</td><td style=";">$C$9</td><td style=";">$D$9</td><td style=";">hhh</td></tr></tbody></table><p style="width:3.6em;font-weight:bold;margin:0;padding:0.2em 0.6em 0.2em 0.5em;border: 1px solid #A6AAB6;border-top:none;text-align: center;background-color: #E0E0F0;color: #161120">Sheet1</p><br /><br />

continued ...
 
Upvote 0
.... Continued

Result:
<b>Excel 2003</b><table cellpadding="2.5px" rules="all" style=";background-color: #FFFFFF;border: 1px solid;border-collapse: collapse; border-color: #A6AAB6"><colgroup><col width="25px" style="background-color: #E0E0F0" /><col /><col /><col /><col /><col /></colgroup><thead><tr style=" background-color: #E0E0F0;text-align: center;color: #161120"><th></th><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th></tr></thead><tbody><tr ><td style="color: #161120;text-align: center;">1</td><td style="text-align: right;;"></td><td style=";">Key</td><td style=";">Data2 / DataB</td><td style=";">Data3 / DataC</td><td style=";">Description / Desc</td></tr><tr ><td style="color: #161120;text-align: center;">2</td><td style=";">Changed</td><td style=";">Alpha</td><td style=";">$C$2</td><td style="background-color: #FFFF00;;">$D$2</td><td style=";">zzz</td></tr><tr ><td style="color: #161120;text-align: center;">3</td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="background-color: #FFFF00;;">xxx</td><td style=";">aaa</td></tr><tr ><td style="color: #161120;text-align: center;">4</td><td style=";">Sheet1 only</td><td style="background-color: #C0C0C0;;">Beta</td><td style="background-color: #C0C0C0;;">$C$3</td><td style="background-color: #C0C0C0;;">$D$3</td><td style="background-color: #C0C0C0;;">xxx</td></tr><tr ><td style="color: #161120;text-align: center;">5</td><td style=";">Sheet1 only</td><td style="background-color: #C0C0C0;;">Delta</td><td style="background-color: #C0C0C0;;">$C$5</td><td style="background-color: #C0C0C0;;">$D$5</td><td style="background-color: #C0C0C0;;">vvv</td></tr><tr ><td style="color: #161120;text-align: center;">6</td><td style=";">Sheet1 only</td><td style="background-color: #C0C0C0;;">Epsiln</td><td style="background-color: #C0C0C0;;">$C$6</td><td style="background-color: #C0C0C0;;">$D$6</td><td style="background-color: #C0C0C0;;">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">7</td><td style=";">Changed</td><td style=";">Zeta</td><td style="background-color: #FFFF00;;">$C$7</td><td style=";">$D$7</td><td style=";">nnn</td></tr><tr ><td style="color: #161120;text-align: center;">8</td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="background-color: #FFFF00;;">bbb</td><td style="text-align: right;;"></td><td style=";">fff</td></tr><tr ><td style="color: #161120;text-align: center;">9</td><td style=";">Sheet1 only</td><td style="background-color: #00FF00;;">Iota</td><td style="background-color: #00FF00;;">$C$3</td><td style="background-color: #00FF00;;">$D$3</td><td style="background-color: #00FF00;;">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">10</td><td style=";">Sheet1 only</td><td style="background-color: #00FF00;;">Kappa</td><td style="background-color: #00FF00;;">$C$5</td><td style="background-color: #00FF00;;">$D$5</td><td style="background-color: #00FF00;;">ddd</td></tr><tr ><td style="color: #161120;text-align: center;">11</td><td style=";">Sheet1 only</td><td style="background-color: #00FF00;;">Lambda</td><td style="background-color: #00FF00;;">$C$6</td><td style="background-color: #00FF00;;">$D$6</td><td style="background-color: #00FF00;;">eee</td></tr></tbody></table><p style="width:3.6em;font-weight:bold;margin:0;padding:0.2em 0.6em 0.2em 0.5em;border: 1px solid #A6AAB6;border-top:none;text-align: center;background-color: #E0E0F0;color: #161120">Sheet1</p><br /><br />

Note that the parameters match headings via '=', this is not needed if the headings are the same, the description heading prefixed by the string "(info)" is displayed but NOT included in the comparison.

The unique key field ('Key' in this example) MUST be the first heading defined.
 
Upvote 0
Perhaps this amended veersion may be a little more informative:
Code:
Option Explicit

Dim miMaxColumns As Integer
Dim miaHeadingCols1() As Integer
Dim miaHeadingCols2() As Integer
Dim msaCompareWorkbooks() As String
Dim msResultsSheet As String
Dim msaHeadings1() As String
Dim msaHeadings2() As String
Dim mbaHeadingsInfo() As Boolean
Dim mwbInputs() As Workbook
Dim mwbReport As Workbook
Dim mwsInputs() As Worksheet
Dim mwsReport As Worksheet

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 vaHeadings() As Variant
Dim wsOld As Worksheet, wsNew As Worksheet

If GetParameters = False Then Exit Sub

Set wsOld = mwsInputs(1)
Set wsNew = mwsInputs(2)

If PopulateHeadingColumns(WS:=wsOld, HeadingsTexts:=msaHeadings1, HeadingsColumns:=miaHeadingCols1) = False Then
    Exit Sub
End If

If PopulateHeadingColumns(WS:=wsNew, HeadingsTexts:=msaHeadings2, HeadingsColumns:=miaHeadingCols2) = False Then
    Exit Sub
End If

miMaxColumns = UBound(msaHeadings1) + 1
Set objDictOld = PopulateDictionary(WS:=wsOld, ColumnPositions:=miaHeadingCols1)
Set objDictNew = PopulateDictionary(WS:=wsNew, ColumnPositions:=miaHeadingCols2)

With mwsReport
    .Cells.ClearFormats
    .Cells.ClearContents
End With

ReDim vaHeadings(1 To 1, 1 To UBound(msaHeadings1) + 2)
For iCol = 0 To UBound(msaHeadings1)
    If msaHeadings1(iCol) = msaHeadings2(iCol) Then
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol)
    Else
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol) & " / " & msaHeadings2(iCol)
    End If
Next iCol
mwsReport.Range("A1", mwsReport.Cells(1, UBound(vaHeadings, 2)).Address).Value = vaHeadings

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)
                If mbaHeadingsInfo(iCol - 1) = False Then
                    baChanged(iCol) = True
                    bChanged = True
                End If
            End If
        Next iCol
        If bChanged Then
            lReportRow = lReportRow + 1
            For iCol = 1 To UBound(baChanged)
                If baChanged(iCol) Then
                    With mwsReport
                        .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 mwsReport
                .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) = wsOld.Parent.Name & " only"
        For iCol = 1 To miMaxColumns
            vaOutput(1, iCol + 1) = vaInputOld(1, iCol)
        Next iCol
        
        lReportRow = lReportRow + 1
        With mwsReport
            .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) = wsNew.Parent.Name & " only"
        For iCol = 1 To miMaxColumns
            vaOutput2(1, iCol + 1) = vaInputNew(1, iCol)
        Next iCol
        lReportRow = lReportRow + 1
        With mwsReport
            .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

With Application
    .EnableEvents = False
    .DisplayAlerts = False
End With
mwbInputs(1).Close savechanges:=False
mwbInputs(2).Close savechanges:=False
Application.EnableEvents = True
End Sub
Private Function PopulateDictionary(ByRef WS As Worksheet, ByRef ColumnPositions() As Integer) As Object
Dim iPtr As Integer, iCurCol As Integer
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String
Dim vaItem() As Variant

Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, ColumnPositions(0)).End(xlUp).Row
For lRow = 2 To lRowEnd
    sKey = Trim$(LCase$(CStr(WS.Cells(lRow, ColumnPositions(0)).Value)))
    ReDim vaItem(1 To 1, 1 To UBound(ColumnPositions) + 1)
    For iPtr = 0 To UBound(ColumnPositions)
        iCurCol = ColumnPositions(iPtr)
        vaItem(1, iPtr + 1) = WS.Cells(lRow, iCurCol).Value
    Next iPtr
    On Error Resume Next
    PopulateDictionary.Add key:=sKey, Item:=vaItem
    On Error GoTo 0
Next lRow
End Function

Private Function GetParameters() As Boolean
Dim iPtr As Integer, iPtr1 As Integer
Dim iParamCheck As Integer
Const iParamCompareSheets As Integer = 1
Const iParamResultsSheet As Integer = 2
Const iParamHeadings As Integer = 4

Dim lRow As Long
Dim sCurKey As String
Dim saHeadings() As String, saHeadingsA() As String
Dim vaParameters As Variant
Dim vInputFiles As Variant, vOutputFile As Variant
Dim wsParams As Worksheet, wsTemp As Worksheet


Set wsParams = Nothing
On Error Resume Next
Set wsParams = Sheets("Parameters")
On Error GoTo 0
If wsParams Is Nothing Then
    MsgBox prompt:="Cannot access 'Parameters' sheet", _
            Buttons:=vbOKOnly + vbCritical, _
            Title:="ERROR"
    GetParameters = False
    Exit Function
End If

lRow = wsParams.Cells(Rows.Count, "A").End(xlUp).Row
vaParameters = wsParams.Range("A1:B" & lRow).Value

iParamCheck = 0
For lRow = 2 To UBound(vaParameters, 1)
    sCurKey = Replace(LCase$(CStr(vaParameters(lRow, 1))), " ", "")
    Select Case sCurKey
        
    Case "headings"
        iParamCheck = iParamCheck Or iParamHeadings
        saHeadings = Split(CStr(vaParameters(lRow, 2)), ",")
        
        ReDim msaHeadings1(0 To UBound(saHeadings))
        ReDim msaHeadings2(0 To UBound(saHeadings))
        ReDim miaHeadingCols1(0 To UBound(saHeadings))
        ReDim miaHeadingCols2(0 To UBound(saHeadings))
        ReDim mbaHeadingsInfo(0 To UBound(saHeadings))
        
        For iPtr = 0 To UBound(saHeadings)
            saHeadingsA = Split("=" & saHeadings(iPtr), "=")
            If UBound(saHeadingsA) < 1 Or UBound(saHeadingsA) > 2 Then
                MsgBox prompt:="Invalid headings value", Buttons:=vbOKOnly + vbCritical
                GetParameters = False
                Exit Function
            End If
            ReDim Preserve saHeadingsA(0 To 2)
            saHeadingsA(1) = Trim$(saHeadingsA(1))
            mbaHeadingsInfo(iPtr) = LCase$(Left$(saHeadingsA(1) & "123456", 6)) = "(info)"
            If mbaHeadingsInfo(iPtr) Then saHeadingsA(1) = Mid$(saHeadingsA(1), 7)
            If saHeadingsA(2) = "" Then saHeadingsA(2) = saHeadingsA(1)
            msaHeadings1(iPtr) = saHeadingsA(1)
            msaHeadings2(iPtr) = saHeadingsA(2)
        Next iPtr
    Case Else
        MsgBox prompt:="Unrecognised parameter in row " & lRow, Buttons:=vbOKOnly + vbCritical
        GetParameters = False
        Exit Function
    End Select
Next lRow

vInputFiles = Application.GetOpenFilename(filefilter:="compae workbooks (*.xls*),*.xls*", _
                                     Title:="Please select the two input workbooks", _
                                     MultiSelect:=True)

If IsArray(vInputFiles) Then
    If UBound(vInputFiles) <> 2 Then
        MsgBox prompt:="Please select exactly two input workbooks for comparison", _
                Buttons:=vbOKOnly + vbCritical
        GetParameters = False
        Exit Function
    End If
Else
    MsgBox prompt:="Macro Abandoned", Buttons:=vbOKOnly + vbInformation
    GetParameters = False
    Exit Function
End If

ReDim mwbInputs(1 To 2)
ReDim mwsInputs(1 To 2)
Application.EnableEvents = False
iPtr1 = 2
For iPtr = 1 To 2
    Set mwbInputs(iPtr) = Workbooks.Open(Filename:=vInputFiles(iPtr1), ReadOnly:=True)
    Set mwsInputs(iPtr) = mwbInputs(iPtr).Sheets(1)
    iPtr1 = iPtr1 - 1
Next iPtr

vOutputFile = Application.GetSaveAsFilename(filefilter:="Output Workbook (*.xls*),*.xls*", _
                                            Title:="Please select output workbook or [Cancel]")
If vOutputFile = False Then
    Set mwbReport = Workbooks.Add
Else
    Set mwbReport = Workbooks.Open(Filename:=vOutputFile, ReadOnly:=False)
    Application.EnableEvents = True
End If
Application.EnableEvents = True
Set mwsReport = mwbReport.Sheets(1)

GetParameters = True
End Function

Private Function PopulateHeadingColumns(ByVal WS As Worksheet, _
                                        ByRef HeadingsTexts() As String, _
                                        ByRef HeadingsColumns() As Integer) As Boolean
Dim bFound As Boolean
Dim iPtrCol As Integer, iPtrHeading As Integer, iColEnd As Integer
Dim sCurHeading As String, sCur As String
Dim vaHeadings() As Variant

iColEnd = WS.Cells(1, Columns.Count).End(xlToLeft).Column
vaHeadings = WS.Range("A1:" & WS.Cells(1, iColEnd).Address).Value

For iPtrHeading = LBound(HeadingsTexts) To UBound(HeadingsTexts)
    sCurHeading = Replace(LCase$(HeadingsTexts(iPtrHeading)), " ", "")
    bFound = False
    For iPtrCol = 1 To UBound(vaHeadings, 2)
        If sCurHeading = Replace(LCase$(CStr(vaHeadings(1, iPtrCol))), " ", "") Then
            HeadingsColumns(iPtrHeading) = iPtrCol
            bFound = True
            Exit For
        End If
    Next iPtrCol
    If bFound = False Then
        MsgBox prompt:="Heading '" & HeadingsTexts(iPtrHeading) & "' not found in " & WS.Parent.Name, _
                Buttons:=vbOKOnly + vbCritical
                PopulateHeadingColumns = False
                Exit Function
    End If
Next iPtrHeading

PopulateHeadingColumns = True
End Function

Which gives this result:
<b>Excel 2003</b><table cellpadding="2.5px" rules="all" style=";background-color: #FFFFFF;border: 1px solid;border-collapse: collapse; border-color: #A6AAB6"><colgroup><col width="25px" style="background-color: #E0E0F0" /><col /><col /><col /><col /><col /></colgroup><thead><tr style=" background-color: #E0E0F0;text-align: center;color: #161120"><th></th><th>A</th><th>B</th><th>C</th><th>D</th><th>E</th></tr></thead><tbody><tr ><td style="color: #161120;text-align: center;">1</td><td style="text-align: right;;"></td><td style=";">Key</td><td style=";">Data2 / DataB</td><td style=";">Data3 / DataC</td><td style=";">Description / Desc</td></tr><tr ><td style="color: #161120;text-align: center;">2</td><td style=";">Changed</td><td style=";">Alpha</td><td style=";">$C$2</td><td style="background-color: #FFFF00;;">$D$2</td><td style=";">zzz</td></tr><tr ><td style="color: #161120;text-align: center;">3</td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="background-color: #FFFF00;;">xxx</td><td style=";">aaa</td></tr><tr ><td style="color: #161120;text-align: center;">4</td><td style=";">Compare1.xls only</td><td style="background-color: #C0C0C0;;">Beta</td><td style="background-color: #C0C0C0;;">$C$3</td><td style="background-color: #C0C0C0;;">$D$3</td><td style="background-color: #C0C0C0;;">xxx</td></tr><tr ><td style="color: #161120;text-align: center;">5</td><td style=";">Compare1.xls only</td><td style="background-color: #C0C0C0;;">Delta</td><td style="background-color: #C0C0C0;;">$C$5</td><td style="background-color: #C0C0C0;;">$D$5</td><td style="background-color: #C0C0C0;;">vvv</td></tr><tr ><td style="color: #161120;text-align: center;">6</td><td style=";">Compare1.xls only</td><td style="background-color: #C0C0C0;;">Epsiln</td><td style="background-color: #C0C0C0;;">$C$6</td><td style="background-color: #C0C0C0;;">$D$6</td><td style="background-color: #C0C0C0;;">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">7</td><td style=";">Changed</td><td style=";">Zeta</td><td style="background-color: #FFFF00;;">$C$7</td><td style=";">$D$7</td><td style=";">nnn</td></tr><tr ><td style="color: #161120;text-align: center;">8</td><td style="text-align: right;;"></td><td style="text-align: right;;"></td><td style="background-color: #FFFF00;;">bbb</td><td style="text-align: right;;"></td><td style=";">fff</td></tr><tr ><td style="color: #161120;text-align: center;">9</td><td style=";">Compare2.xls only</td><td style="background-color: #00FF00;;">Iota</td><td style="background-color: #00FF00;;">$C$3</td><td style="background-color: #00FF00;;">$D$3</td><td style="background-color: #00FF00;;">bbb</td></tr><tr ><td style="color: #161120;text-align: center;">10</td><td style=";">Compare2.xls only</td><td style="background-color: #00FF00;;">Kappa</td><td style="background-color: #00FF00;;">$C$5</td><td style="background-color: #00FF00;;">$D$5</td><td style="background-color: #00FF00;;">ddd</td></tr><tr ><td style="color: #161120;text-align: center;">11</td><td style=";">Compare2.xls only</td><td style="background-color: #00FF00;;">Lambda</td><td style="background-color: #00FF00;;">$C$6</td><td style="background-color: #00FF00;;">$D$6</td><td style="background-color: #00FF00;;">eee</td></tr></tbody></table><p style="width:3.6em;font-weight:bold;margin:0;padding:0.2em 0.6em 0.2em 0.5em;border: 1px solid #A6AAB6;border-top:none;text-align: center;background-color: #E0E0F0;color: #161120">Sheet1</p><br /><br />
 
Upvote 0
Thanks again 'al_b_cnu'! You are a GREAT help! You really know your stuff!

After further consideration, I think it best if I keep everything in 1(one) workbook and on 3 sheets (as per your original code, though what you did could be used later in another project).

ONE MORE QUESTION (if you don't mind):
Does the original code only work with 5 columns of data (I tried 1 column and 8 columns and it did not work)? What if I want 9 consecutive columns or just 1 column or 3 to compare with (assuming the headers and the number of columns in sheets 1 and 2 are the same)? What parameters need to be changed as you change the number of columns with data in it to be compared? Would it be the parmeter sheet you described for the previous code? I just need for it to work with one work book comparing 2 different sheets.

Thanks!
 
Upvote 0
After doing a little more looking around on your posts, I found this post which does what I need for it to do!

which code is:

Code:
Option Explicit

Dim miMaxColumns As Integer
Dim miaHeadingCols1() As Integer
Dim miaHeadingCols2() As Integer
Dim msaCompareSheets() As String
Dim msResultsSheet As String
Dim msaHeadings1() As String
Dim msaHeadings2() As String
Dim mbaHeadingsInfo() As Boolean
Dim mwsInputs() As Worksheet

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 vaHeadings() As Variant
Dim wsOld As Worksheet, wsNew As Worksheet, wsReport As Worksheet

If GetParameters = False Then Exit Sub

Set wsOld = mwsInputs(0)
Set wsNew = mwsInputs(1)

If PopulateHeadingColumns(WS:=wsOld, HeadingsTexts:=msaHeadings1, HeadingsColumns:=miaHeadingCols1) = False Then
    Exit Sub
End If

If PopulateHeadingColumns(WS:=wsNew, HeadingsTexts:=msaHeadings2, HeadingsColumns:=miaHeadingCols2) = False Then
    Exit Sub
End If

miMaxColumns = UBound(msaHeadings1) + 1
Set objDictOld = PopulateDictionary(WS:=wsOld, ColumnPositions:=miaHeadingCols1)
Set objDictNew = PopulateDictionary(WS:=wsNew, ColumnPositions:=miaHeadingCols2)

Set wsReport = Sheets(msResultsSheet)

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

ReDim vaHeadings(1 To 1, 1 To UBound(msaHeadings1) + 2)
For iCol = 0 To UBound(msaHeadings1)
    If msaHeadings1(iCol) = msaHeadings2(iCol) Then
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol)
    Else
        vaHeadings(1, iCol + 2) = msaHeadings1(iCol) & " / " & msaHeadings2(iCol)
    End If
Next iCol
wsReport.Range("A1", wsReport.Cells(1, UBound(vaHeadings, 2)).Address).Value = vaHeadings

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)
                If mbaHeadingsInfo(iCol - 1) = False Then
                    baChanged(iCol) = True
                    bChanged = True
                End If
            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) = wsOld.Name & " only"
        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) = wsNew.Name & " only"
        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, ByRef ColumnPositions() As Integer) As Object
Dim iPtr As Integer, iCurCol As Integer
Dim lRowEnd As Long, lRow As Long
Dim rCur As Range
Dim sKey As String
Dim vaItem() As Variant

Set PopulateDictionary = Nothing
Set PopulateDictionary = CreateObject("Scripting.Dictionary")
lRowEnd = WS.Cells(Rows.Count, ColumnPositions(0)).End(xlUp).Row
For lRow = 2 To lRowEnd
    sKey = Trim$(LCase$(CStr(WS.Cells(lRow, ColumnPositions(0)).Value)))
    ReDim vaItem(1 To 1, 1 To UBound(ColumnPositions) + 1)
    For iPtr = 0 To UBound(ColumnPositions)
        iCurCol = ColumnPositions(iPtr)
        vaItem(1, iPtr + 1) = WS.Cells(lRow, iCurCol).Value
    Next iPtr
    On Error Resume Next
    PopulateDictionary.Add key:=sKey, Item:=vaItem
    On Error GoTo 0
Next lRow
End Function

Private Function GetParameters() As Boolean
Dim iPtr As Integer
Dim iParamCheck As Integer
Const iParamCompareSheets As Integer = 1
Const iParamResultsSheet As Integer = 2
Const iParamHeadings As Integer = 4

Dim lRow As Long
Dim sCurKey As String
Dim saHeadings() As String, saHeadingsA() As String
Dim vaParameters As Variant
Dim wsParams As Worksheet, wsTemp As Worksheet

Set wsParams = Nothing
On Error Resume Next
Set wsParams = Sheets("Parameters")
On Error GoTo 0
If wsParams Is Nothing Then
    MsgBox prompt:="Cannot access 'Parameters' sheet", _
            Buttons:=vbOKOnly + vbCritical, _
            Title:="ERROR"
    GetParameters = False
    Exit Function
End If

lRow = wsParams.Cells(Rows.Count, "A").End(xlUp).Row
vaParameters = wsParams.Range("A1:B" & lRow).Value

iParamCheck = 0
For lRow = 2 To UBound(vaParameters, 1)
    sCurKey = Replace(LCase$(CStr(vaParameters(lRow, 1))), " ", "")
    Select Case sCurKey
    Case "comparesheets"
        iParamCheck = iParamCheck Or iParamCompareSheets
        msaCompareSheets = Split(CStr(vaParameters(lRow, 2)), ",")
        If UBound(msaCompareSheets) <> 1 Then
            MsgBox prompt:="'Compare Sheets' parameter has too many elements", Buttons:=vbOKOnly + vbCritical
            GetParameters = False
            Exit Function
        End If
        ReDim mwsInputs(0 To 1)
        For iPtr = 0 To 1
            Set mwsInputs(iPtr) = Nothing
            On Error Resume Next
            Set mwsInputs(iPtr) = Sheets(msaCompareSheets(iPtr))
            On Error GoTo 0
            If mwsInputs(iPtr) Is Nothing Then
                MsgBox prompt:="Cannot access compare sheet '" & msaCompareSheets(iPtr) & "'", Buttons:=vbOKOnly + vbCritical
                GetParameters = False
                Exit Function
            End If
        Next iPtr
        
    Case "resultssheet"
        iParamCheck = iParamCheck Or iParamResultsSheet
        msResultsSheet = CStr(vaParameters(lRow, 2))
        Set wsTemp = Nothing
        On Error Resume Next
        Set wsTemp = Sheets(msResultsSheet)
        On Error GoTo 0
        If wsTemp Is Nothing Then
            MsgBox prompt:="Cannot access Results sheet '" & msResultsSheet & "'", Buttons:=vbOKOnly + vbCritical
            GetParameters = False
            Exit Function
        End If
        
    Case "headings"
        iParamCheck = iParamCheck Or iParamHeadings
        saHeadings = Split(CStr(vaParameters(lRow, 2)), ",")
        
        ReDim msaHeadings1(0 To UBound(saHeadings))
        ReDim msaHeadings2(0 To UBound(saHeadings))
        ReDim miaHeadingCols1(0 To UBound(saHeadings))
        ReDim miaHeadingCols2(0 To UBound(saHeadings))
        ReDim mbaHeadingsInfo(0 To UBound(saHeadings))
        
        For iPtr = 0 To UBound(saHeadings)
            saHeadingsA = Split("=" & saHeadings(iPtr), "=")
            If UBound(saHeadingsA) < 1 Or UBound(saHeadingsA) > 2 Then
                MsgBox prompt:="Invalid headings value", Buttons:=vbOKOnly + vbCritical
                GetParameters = False
                Exit Function
            End If
            ReDim Preserve saHeadingsA(0 To 2)
            mbaHeadingsInfo(iPtr) = LCase$(Left$(saHeadingsA(1) & "123456", 6)) = "(info)"
            If mbaHeadingsInfo(iPtr) Then saHeadingsA(1) = Mid$(saHeadingsA(1), 7)
            If saHeadingsA(2) = "" Then saHeadingsA(2) = saHeadingsA(1)
            msaHeadings1(iPtr) = saHeadingsA(1)
            msaHeadings2(iPtr) = saHeadingsA(2)
        Next iPtr
    Case Else
        MsgBox prompt:="Unrecognised parameter in row " & lRow, Buttons:=vbOKOnly + vbCritical
        GetParameters = False
        Exit Function
    End Select
Next lRow

If iParamCheck <> 7 Then
    MsgBox prompt:="Missing parameter(s)!", Buttons:=vbOKOnly + vbCritical
    GetParameters = False
    Exit Function
End If

GetParameters = True
End Function

Private Function PopulateHeadingColumns(ByVal WS As Worksheet, _
                                        ByRef HeadingsTexts() As String, _
                                        ByRef HeadingsColumns() As Integer) As Boolean
Dim bFound As Boolean
Dim iPtrCol As Integer, iPtrHeading As Integer, iColEnd As Integer
Dim sCurHeading As String, sCur As String
Dim vaHeadings() As Variant

iColEnd = WS.Cells(1, Columns.Count).End(xlToLeft).Column
vaHeadings = WS.Range("A1:" & WS.Cells(1, iColEnd).Address).Value

For iPtrHeading = LBound(HeadingsTexts) To UBound(HeadingsTexts)
    sCurHeading = Replace(LCase$(HeadingsTexts(iPtrHeading)), " ", "")
    bFound = False
    For iPtrCol = 1 To UBound(vaHeadings, 2)
        If sCurHeading = Replace(LCase$(CStr(vaHeadings(1, iPtrCol))), " ", "") Then
            HeadingsColumns(iPtrHeading) = iPtrCol
            bFound = True
            Exit For
        End If
    Next iPtrCol
    If bFound = False Then
        MsgBox prompt:="Heading '" & HeadingsTexts(iPtrHeading) & "' not found in sheet '" & WS.Name, _
                Buttons:=vbOKOnly + vbCritical
                PopulateHeadingColumns = False
                Exit Function
    End If
Next iPtrHeading

PopulateHeadingColumns = True
End Function
I think where it says "sheet1 only" your new code says "Deleted" and where it says "sheet2 only" your new code says "Inserted". Is this correct???

What would you change on the code referenced above to make the 'results' column to say what your new code says?
 
Upvote 0
Would it be safe to change the following or am I missing something:

line 104
vaOutput(1, 1) = wsOld.Name & " only"
to
vaOutput(1, 1) = "Deleted"

and

line 123
vaOutput2(1, 1) = wsNew.Name & " only"
to
vaOutput2(1, 1) = "Inserted"

Thanks again!!
 
Upvote 0
hi IrishMist,

You've probably figured it out yourself, and yes, your proposed amendments look good to me.
I think those changes are in the macro as shown in post #19 here: http://www.mrexcel.com/forum/showthread.php?t=524877&page=2

The Headings parameter defines which columns are to be displayed & compared.
rules for the Headings value are
Only headings defined are displayed / compared
A comma separates each heading in the list
The first heading MUST define the unique key column
Different headings which are equivalent can be married up by defining the first heading followed by "=" followed by the second heading
Headings which are NOT to be compared, but which are required to be displayed must be prefixed by the string "(info)"

For example if sheet1 contains the headings Personnel Num, First Name, Surname, DOB, Comments
and sheet2 contains the headings Personnel No, first Name, Last Name, Comments, Department
to match on personnell number, name, and show Comments but not match on it, the headings parameter would be:
Personnel Num=Personnel No, first Name, Surname=Last Name,(info)Comments
note that 'Personnel Num' is married up to 'Personnel No', 'Surname' is married up to 'Last Name' and the comments will be displayed but not compared
 
Upvote 0

Forum statistics

Threads
1,215,731
Messages
6,126,535
Members
449,316
Latest member
sravya

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