Is there a good way to speed up this VBA code? - excel

I have to run this code on a sheet of about 5000+ rows. At this point I could do it faster manually. I need to add a new row, carry a few values down from the previous row, create subtotals, and reshade wherever there is a change in column 'G'. This code will start in row 8 and only needs to be applied to cells E:X. Is there a better way to do this?
On further testing it seems the issue is that I'm having to add hundreds of rows individually. Is there a way to find all the rows where the value is not equal to the one above and add all rows en masse?
Sub subtotals()
'counter variables
cs = 8
c = 8
Do Until Range("E" & r) = ""
c = r
cs = r
'Do until Material Column does not equal material above
Do Until Range("g" & r) <> Range("g" & r + 1)
c = c + 1
r = r + 1
Loop
r = r + 1
Rows(r).Insert
'total label in SECTION
x = "e"
Range(x & r) = "Total"
x = "q"
Range(x & r).Formula = "=sum(" & x & cs & ":" & x & c & ")"
'rows to shade
Range("E" & r, "x" & r).Locked = True
Range("E" & r, "x" & r).Select
'shading
With Selection.Interior
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorDark2
.TintAndShade = -0.499984740745262
.PatternTintAndShade = 0
End With
With Selection.Font
.ThemeColor = xlThemeColorDark1
.TintAndShade = 0
.Bold = True
End With
Selection.HorizontalAlignment = xlCenter
r = r + 1
Loop
End Sub

Insert Subtotals
Up to a thousand inserted rows this will behave i.e. it'll take a few seconds. After that, it might take forever.
Try implementing Application.Calculation and Application.ScreenUpdating into your code. Its usage is pretty straightforward. It will speed up your code.
Option Explicit
Sub InsertSubtotals()
Const wsName As String = "Sheet1" ' adjust
Const fRow As Long = 8 ' First Row
Const tCol As String = "E" ' Total Column
Const cCol As String = "G" ' Criteria (Search) Column
Const fCol As String = "Q" ' Formula Column
Const fCols As String = "E:X" ' Format Columns
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, tCol).End(xlUp).Row
If lRow < fRow Then Exit Sub ' no data
Dim pRow As Long: pRow = lRow + 1
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim trg As Range ' Total Range
Dim OldValue As Variant
Dim NewValue As Variant
Dim r As Long
Dim pFormula As String
For r = pRow To fRow + 1 Step -1
NewValue = ws.Cells(r - 1, cCol).Value
If StrComp(CStr(NewValue), CStr(OldValue), vbTextCompare) <> 0 Then
If pRow > r Then
WriteFormula ws, r, pRow, fCol
pRow = r
End If
ws.Rows(r).Insert
If Not trg Is Nothing Then
Set trg = Union(trg, ws.Cells(r, tCol))
Else
Set trg = ws.Cells(r, tCol)
End If
OldValue = NewValue
End If
Next r
WriteFormula ws, fRow, pRow, fCol
' Write 'Total' in one go.
trg.Value = "Total"
' Apply formatting in one go.
With Intersect(trg.EntireRow, ws.Columns(fCols))
.Locked = True
With .Interior
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorDark2
.TintAndShade = -0.499984740745262
.PatternTintAndShade = 0
End With
With .Font
.ThemeColor = xlThemeColorDark1
.TintAndShade = 0
.Bold = True
End With
.HorizontalAlignment = xlCenter
End With
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Sub WriteFormula( _
ByVal ws As Worksheet, _
ByVal r As Long, _
ByVal pRow As Long, _
ByVal ColumnString As String)
Dim pFormula As String
pFormula = "=SUM(" & ColumnString & r & ":" & ColumnString & pRow - 1 & ")"
ws.Cells(pRow, ColumnString).Formula = pFormula
End Sub

Related

How to unmerge rows and not column

I have a table which contains merged cells both column and rows as shown in attached picture. I want to unmerge "Only" rows while leaving columns merged. Consider the following snippet of table. In the image attached "Contract
For y = 1 To lRow
p = 1
c = y
d = 1
z = lRow + y
t = Cells(y, 1).Value
For x = 1 To t
Cells(z, p).Value = Cells(c, d).Value
Cells(c, d).Select
' Debug.Print
Selection.End(xlToRight).Select
c = ActiveCell.Row
d = ActiveCell.Column
p = p + 1
Next
Next
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
With c.MergeArea.Rows
.UnMerge
' .Formula = c.Formula
End With
'
'startcolumn = ActiveCell.Column
'endcolumn = Selection.Columns.Count + startcolumn - 1
'startrow = ActiveCell.Row
'endrow = Selection.Rows.Count + startrow - 1
End If
Next
End Sub
Based on your snapshot of requirements , I have wrote a very simple code which shall appear to be crude but I have kept it this way so that you can adjust its various elements as per your actual data. Sample data taken by me and results obtained are shown in the snapshot pasted below, which is followed by code.
Sub Merge_unmerge()
Dim wb As Workbook
Dim ws As Worksheet
Dim rng As Range
Dim cell As Range
Dim LastRow As Long
Dim LastCol As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
With ws
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
LastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
Set rng = ws.Range("A1:D" & LastRow)
For Each cell In rng
cell.UnMerge
Next cell
For i = 2 To LastRow
If Range("A" & i) = "" Then
Range("A" & i).Value = Range("A" & i - 1).Value
End If
Next i
For i = 2 To LastRow
If Range("D" & i) = "" Then
Range("D" & i).Value = Range("D" & i - 1).Value
End If
Next i
For i = 1 To LastRow Step 2
Range("B" & i & ":C" & i).Merge
Range("B" & i & ":C" & i).HorizontalAlignment = xlCenter
Next i
End Sub
Never mind. I solved for the issue at hand. Posting if it helps others.
Sub ColorMergedCells()
Dim c As Range
Dim startcolumn, endcolumn, startrow, endrow As Long
For Each c In ActiveSheet.UsedRange
If c.MergeCells And c.MergeArea.Rows.Count >= 2 Then
c.Interior.ColorIndex = 28
startcolumn = c.Column
endcolumn = c.MergeArea.Columns.Count + startcolumn - 1
startrow = c.Row
endrow = c.MergeArea.Rows.Count + startrow - 1
With c.MergeArea.Rows
.UnMerge
.Formula = c.Formula
End With
For J = startrow To endrow
Application.DisplayAlerts = False
Range(Cells(J, startcolumn), Cells(J, endcolumn)).Merge
Application.DisplayAlerts = True
Next
End If
Next
End Sub

loop through a filtered range excel vba

I want to highlight cells when there are 3 or more with the same value, I have the bellow code but it isn't working right because it loop twice into each range. Can someone tell me what is wrong?
Sub HighlightCells()
Dim k As Integer, myCounter As Integer, firstRow As Integer
Dim myClientExport As Worksheet, myTemplate As Variant
Dim vRange As Range, myRange As Range
Dim myAddr As String
Set myClientExport = Excel.ActiveSheet
Set vRange = myClientExport.UsedRange.SpecialCells(xlCellTypeVisible)
firstRow = vRange.Areas(2).Rows(1).Row
myAddr = Cells(firstRow, 4).Value
myCounter = 0
For Each myRange In vRange.Rows
k = myRange.Row
If k > firstRow Then
If myAddr = Cells(k, 4).Value Then
myCounter = myCounter + 1
Else
myAddr = Cells(k, 4).Value
myCounter = 0
End If
Select Case myCounter
Case 3
For i = 0 To 2
OId = Cells(k - i, 1).Value
Next i
Cells(k, 4).Interior.ColorIndex = 27
Cells(k - 1, 4).Interior.ColorIndex = 27
Cells(k - 2, 4).Interior.ColorIndex = 27
Case Is > 3
Cells(k, 4).Interior.ColorIndex = 27
End Select
End If
Next myRange
End Sub
My data unfiltered look like:
My data filtered look like
I want to highlight all Address with 3 or more Active order status.
I can't get your code to work. Right off I get "Application-defined or object-defined" run-time error on firstRow = vRange.Areas(2).Rows(1).Row.
So what I did is a CountIf formula in column E:
=COUNTIF(D$2:D$7,D2) then set Conditional Formatting. If the range is dynamic, use VBA to determine limits and propagate the formula and Conditional Formatting. Something like:
Dim rCount As Integer
rCount = Range("D1", Range("D2").End(xlDown)).Rows.Count
Range("D2:D" & rCount).Select
Selection.Cells.FormatConditions.Delete
Selection.FormatConditions.Add Type:=xlExpression, Formula1:="=$E2=3"
Selection.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With Selection.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
End With
Selection.FormatConditions(1).StopIfTrue = False
Range("E2:E" & rCount).Select
Selection.Formula = "=CountIf($D$2:$D$" & rCount & ", $D2)"
You may try something like this to see if that works for you.
The code will place a CountIFs formula in column F and delete it in the end.
You may tweak the code as per your requirement.
Sub HighlightFilteredCells()
Dim sws As Worksheet
Dim lr As Long
Dim cell As Range
Set sws = Sheets("Sheet1")
If sws.FilterMode Then sws.ShowAllData
lr = Cells(Rows.Count, 1).End(xlUp).Row
sws.Range("F2:F" & lr).Formula = "=COUNTIFS($D$2:$D$" & lr & ",D2,$E$2:$E$" & lr & ",""Active"")"
sws.Columns(5).Interior.ColorIndex = xlNone
With sws.Rows(1)
.AutoFilter field:=5, Criteria1:="Active"
If sws.Range("A1").CurrentRegion.SpecialCells(xlCellTypeVisible).Rows.Count > 1 Then
For Each cell In sws.Range("F2:F" & lr).SpecialCells(xlCellTypeVisible)
If cell.Value >= 3 Then cell.Offset(0, -1).Interior.Color = vbYellow
Next cell
End If
End With
sws.Columns(6).Clear
End Sub

compare 2 excel sheets for differences

I need to compare 2 excel sheets (Sheet1 (old report) & Sheet2 (new report)) for differences. If there are any additions or removals in Sheet2 compared to Sheet1 I need to print that.
I found this script to find the differences but this is not including the removals in the sheet. Can you help fixing this? Below is sample example on my expectation.
Sheet1:
S.No Name Class
abc1 1st
abc2 1st
abc3 1st
Sheet2:
S.No Name Class
abc1 1st
abc2 2nd
abc4 1st
.
Comparison should tell all these:
"Row(3,3)" is changed from "1st" to "2nd"
New row inserted in "sheet2" "Row4"
"Sheet1" "Row4" is deleted in "Sheet2"
Script currently I have:
Sub Compare2Shts()
For Each cell In Worksheets("CompareSheet#1").UsedRange
If cell.Value <> Worksheets("CompareSheet#2").Range(cell.Address) Then
cell.Interior.ColorIndex = 3
End If
Next
For Each cell In Worksheets("CompareSheet#2").UsedRange
If cell.Value <> Worksheets("CompareSheet#1").Range(cell.Address) Then
cell.Interior.ColorIndex = 3
End If
Next
End Sub
Sub CompareAnother2Shts()
For Each cell In Worksheets("CompareSheet#1").Range("A1:J50000")
If cell.Value <> Worksheets("CompareSheet#2").Range(cell.Address) Then
cell.Interior.ColorIndex = 3
End If
Next
For Each cell In Worksheets("CompareSheet#2").Range("A1:J50000")
If cell.Value <> Worksheets("CompareSheet#1").Range(cell.Address) Then
cell.Interior.ColorIndex = 3
End If
Next
End Sub
Sub FindDupes() 'assuming both sheets are in same book and book is open
Dim sht1 As Worksheet
Dim sht2 As Worksheet
Dim cell1 As Range
Dim cell2 As Range
Dim str As String
str = InputBox("Type name of first sheet")
Set sht1 = Worksheets(str)
str = InputBox("Type name of second sheet")
Set sht2 = Worksheets(str)
sht1.Range("A65536").End(xlDown).Activate
Selection.End(xlUp).Activate
LastRowSht1 = ActiveCell.Row
sht2.Activate
sht2.Range("A65536").End(xlDown).Activate
Selection.End(xlUp).Activate
LastRowSht2 = ActiveCell.Row
sht1.Activate
For rowSht1 = 1 To LastRowSht1
If sht1.Cells(rowSht1, 1) = "" Then Exit Sub
For rowSht2 = 1 To LastRowSht2
If sht1.Cells(rowSht1, 1).Value = sht2.Cells(rowSht2, 1).Value Then
sht1.Cells(rowSht1, 1).Interior.ColorIndex = 3
sht2.Cells(rowSht2, 1).Interior.ColorIndex = 3
End If
Next
Next
sht1.Cells(1, 1).Select
End Sub
******** ******** ******** ******** ******** ******** ******** ********
Sub checkrev()
With Sheets("Sheet1")
Sh1LastRow = .Cells(Rows.Count, "A").End(xlUp).Row
Set Sh1Range = .Range("A1:A" & Sh1LastRow)
End With
With Sheets("Sheet2")
Sh2LastRow = .Cells(Rows.Count, "A").End(xlUp).Row
Set Sh2Range = .Range("A1:A" & Sh2LastRow)
End With
'compare sheet 1 with sheet 2
For Each Sh1cell In Sh1Range
Set c = Sh2Range.Find( _
what:=Sh1cell, LookIn:=xlValues)
If c Is Nothing Then
Sh1cell.Interior.ColorIndex = 3
Sh1cell.Offset(0, 1).Interior.ColorIndex = 3
Else
If Sh1cell.Offset(0, 1) <> c.Offset(0, 1) Then
Sh1cell.Interior.ColorIndex = 6
Sh1cell.Offset(0, 1).Interior.ColorIndex = 6
End If
End If
Next Sh1cell
'compare sheet 2 with sheet 1
For Each Sh2cell In Sh2Range
Set c = Sh1Range.Find( _
what:=Sh2cell, LookIn:=xlValues)
If c Is Nothing Then
Sh2cell.Interior.ColorIndex = 3
Sh2cell.Offset(0, 1).Interior.ColorIndex = 3
Else
If Sh2cell.Offset(0, 1) <> c.Offset(0, 1) Then
Sh2cell.Interior.ColorIndex = 6
Sh2cell.Offset(0, 1).Interior.ColorIndex = 6
End If
End If
Next Sh2cell
End Sub
******** ******** ******** ******** ******** ******** ******** ********
Sub TestCompareWorksheets()
' compare two different worksheets in the active workbook
CompareWorksheets Worksheets("Sheet1"), Worksheets("Sheet2")
' compare two different worksheets in two different workbooks
' CompareWorksheets ActiveWorkbook.Worksheets("Sheet1"), _
Workbooks("WorkBookName.xls").Worksheets("Sheet2")
End Sub
Sub CompareWorksheets(ws1 As Worksheet, ws2 As Worksheet)
Dim r As Long, c As Integer
Dim lr1 As Long, lr2 As Long, lc1 As Integer, lc2 As Integer
Dim maxR As Long, maxC As Integer, cf1 As String, cf2 As String
Dim rptWB As Workbook, DiffCount As Long
Application.ScreenUpdating = False
Application.StatusBar = "Creating the report..."
Set rptWB = Workbooks.Add
Application.DisplayAlerts = False
While Worksheets.Count > 1
Worksheets(2).Delete
Wend
Application.DisplayAlerts = True
With ws1.UsedRange
lr1 = .Rows.Count
lc1 = .Columns.Count
End With
With ws2.UsedRange
lr2 = .Rows.Count
lc2 = .Columns.Count
End With
maxR = lr1
maxC = lc1
If maxR < lr2 Then maxR = lr2
If maxC < lc2 Then maxC = lc2
DiffCount = 0
For c = 1 To maxC
Application.StatusBar = "Comparing cells " & Format(c / maxC, "0 %") & "..."
For r = 1 To maxR
cf1 = ""
cf2 = ""
On Error Resume Next
cf1 = ws1.Cells(r, c).FormulaLocal
cf2 = ws2.Cells(r, c).FormulaLocal
On Error GoTo 0
If cf1 <> cf2 Then
DiffCount = DiffCount + 1
Cells(r, c).Formula = "'" & cf1 & " <> " & cf2
End If
Next r
Next c
Application.StatusBar = "Formatting the report..."
With Range(Cells(1, 1), Cells(maxR, maxC))
.Interior.ColorIndex = 19
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
On Error Resume Next
With .Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
On Error GoTo 0
End With
Columns("A:IV").ColumnWidth = 20
rptWB.Saved = True
If DiffCount = 0 Then
rptWB.Close False
End If
Set rptWB = Nothing
Application.StatusBar = False
Application.ScreenUpdating = True
MsgBox DiffCount & " cells contain different formulas!", vbInformation, _
"Compare " & ws1.Name & " with " & ws2.Name
End Sub
******** ******** ******** ******** ******** ******** ******** ********
Sub Match()
r1 = Worksheets("sheet1").Cells(Rows.Count, "A").End(xlUp).Row
r2 = Worksheets("sheet2").Cells(Rows.Count, "A").End(xlUp).Row
Set r3 = Worksheets("sheet1")
Worksheets("sheet2").Range("B2").Select
For a = 2 To r2
For i = 2 To r1
If Cells(a, "A") = r3.Cells(i, "A") Then
temp = r3.Cells(i, "B")
te = te & "," & temp
Else
End If
Next i
Cells(a, "B") = te
te = ""
Next a
End Sub
Sub Match2()
Dim myCon As String
Dim myCell As Range
Dim cell As Range
For Each cell In Sheet2.Range("A2:A10")
myCon = ""
For Each myCell In Sheet1.Range("A1:A15")
If cell = myCell Then
If myCon = "" Then
myCon = myCell.Offset(0, 1)
Else
myCon = myCon & ", " & myCell.Offset(0, 1)
End If
End If
Next myCell
cell.Offset(0, 1) = myCon
Next cell
End Sub
******** ******** ******** ******** ******** ******** ******** ********
Sub Duplicates()
ScreenUpdating = False
'get first empty row of sheet1
'find matching rows in sheet 2
With Sheets("Masterfile")
RowCount = 1
Do While .Range("A" & RowCount) <> ""
ID = Trim(.Range("A" & RowCount))
'compare - look for ID in Sheet 2
With Sheets("List")
Set c = .Columns("A").Find(what:=ID, _
LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
End With
If c Is Nothing Then
.Range("B" & RowCount) = "No"
Else
.Range("B" & RowCount) = "Yes"
End If
RowCount = RowCount + 1
Loop
End With
ScreenUpdating = True
End Sub
The code you have looks overly complex.
For a non-vba solution, see below.
Sheet 1 formula:
=IF(ISERROR(VLOOKUP(A2,Sheet2!$A$2:$B$4,2,0)),"Removed",IF(VLOOKUP(A2,Sheet2!$A$2:$B$4,2,0)=B2,"Same","Changed to: " &VLOOKUP(A2,Sheet2!$A$2:$B$4,2,0)))
Sheet 2 formula:
=IF(ISERROR(VLOOKUP(A2,Sheet1!$A$2:$B$4,2,0)),"Added",IF(VLOOKUP(A2,Sheet1!$A$2:$B$4,2,0)=B2,"Same","Changed"))
I realize I may haved simplified things a bit, but you can adjust wording and whatever is needed. You can also apply conditional formatting as needed.

Speeding up For Loop which contains calculations

Changed code to this (entire module is here)
Sub Filter_TPDrop()
'
' Filter based on Voids and < 5 min times
'
Dim LstRow, i, TestVoid, TestTime As Long
Dim ActiveDate As Variant
Dim NewData, delRange As Range
Dim T1, T2 As Date
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
ActiveDate = Worksheets("TPDrop").Range("H2").Value
'
' Sort the Table by location and cheque open time
Worksheets("TPDrop").Range("A1").Sort _
Key1:=Worksheets("TPDrop").Columns("A"), Header:=xlYes, _
Key2:=Worksheets("TPDRop").Columns("I"), Header:=xlYes
Worksheets("TPDrop").Range("A1").Select
' Find last row of Data
With ActiveSheet.UsedRange
LstRow = .Rows(.Rows.Count).Row
End With
' Delete Any Row where K,L and M = 0 (Void) and where chqtime , 5 min
For i = 2 To LstRow
TestVoid = (Range("K" & i).Value + Range("L" & i).Value + Range("M" & i).Value)
T1 = (Range("I" & i).Value)
T2 = (Range("J" & i).Value)
TestTime = DateDiff("n", T1, T2)
If TestVoid = 0 Or TestTime < 5 Then
Set delRange = Rows(i)
Else
Set delRange = Union(delRange, Rows(i))
End If
Next i
If Not delRange Is Nothing Then delRange.Delete shift:=xlUp
' reset LstRow after filtering and put line between locations
With ActiveSheet.UsedRange
LstRow = .Rows(.Rows.Count).Row
End With
Set NewData = ActiveSheet.UsedRange
For i = LstRow To 3 Step -1
If NewData.Cells(i, 1).Value <> NewData.Cells(i - 1, 1).Value Then
NewData.Cells(i, 1).EntireRow.Insert
End If
Next i
'
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
This snippet of code works but is taking about 4 minutes to run through 6400 lines. I'm not familiar with arrays but
understand through reading other posts that using them could greatly speed up this section of code. Anyone have any
suggestions?
Sub Filter_TPDrop()
'
' Filter based on Voids and < 5 min times
'
Dim LstRow, i, TestVoid, TestTime As Long
Dim ActiveDate As Variant
Dim NewData As Range
Dim T1, T2 As Date
With Application
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
ActiveDate = Worksheets("TPDrop").Range("H2").Value
'
' Sort the Table by location and cheque open time
Worksheets("TPDrop").Range("A1").Sort _
Key1:=Worksheets("TPDrop").Columns("A"), Header:=xlYes, _
Key2:=Worksheets("TPDRop").Columns("I"), Header:=xlYes
Worksheets("TPDrop").Range("A1").Select
' Find last row of Data
With ActiveSheet.UsedRange
LstRow = .Rows(.Rows.Count).Row
End With
' Delete Any Row where K,L and M = 0 (Void) and where chqtime < 5 min
For i = LstRow To 2 Step -1
TestVoid = (Range("K" & i).Value + Range("L" & i).Value _
+ Range("M" & i).Value)
T1 = (Range("I" & i).Value)
T2 = (Range("J" & i).Value)
TestTime = DateDiff("n", T1, T2)
If TestVoid = 0 _
Or TestTime < 5 _
Then Rows(i).Delete
Next i
End Sub
You are deleting in a loop. See my Answer which does the deletion in the end and not in the loop ;) This will greatly increase your speed.
Change For i = LstRow To 2 Step -1 to For i = 2 To LstRow
and replace
If TestVoid = 0 _
Or TestTime < 5 _
Then Rows(i).Delete
by
If TestVoid = 0 Or TestTime < 5 Then
If delRange Is Nothing Then
Set delRange = .Rows(i)
Else
Set delRange = Union(delRange, .Rows(i))
End If
End If
And after Next i, put this line
If Not delRange Is Nothing Then delRange.Delete shift:=xlUp

Comparing rows and reporting back diferences Excel VBA

I've been trying to use the following code to compare two Excel sheets however I can't get it to function quite as I need. I need to compare row by row and report specific differences even when rows of data on each sheet are not necessarily in the same order. The code reports all differences based on how the data physically exists in the tables. So it is showing differences but if the data was put into the same order in each table they wouldn't actually be differences. Due to the nature of the data I can't sort first. Hope this makes sense. Could someone please suggest what changes are required to get what I need?
Sub Compare()
' compare two different worksheets in the active workbook
CompareWorksheets Worksheets("Sheet1"), Worksheets("Sheet2")
' compare two different worksheets in two different workbooks
'CompareWorksheets ActiveWorkbook.Worksheets("Sheet1"), _
' Workbooks("impchk1.xls").Worksheets("Sheet2")
End Sub
Sub CompareWorksheets(ws1 As Worksheet, ws2 As Worksheet)
Dim r As Long, c As Integer
Dim lr1 As Long, lr2 As Long, lc1 As Integer, lc2 As Integer
Dim maxR As Long, maxC As Integer, cf1 As String, cf2 As String
Dim rptWB As Workbook, DiffCount As Long
Application.ScreenUpdating = False
Application.StatusBar = "Creating the report..."
Set rptWB = Workbooks.Add
Application.DisplayAlerts = False
While Worksheets.Count > 1
Worksheets(2).Delete
Wend
Application.DisplayAlerts = True
With ws1.UsedRange
lr1 = .Rows.Count
lc1 = .Columns.Count
End With
With ws2.UsedRange
lr2 = .Rows.Count
lc2 = .Columns.Count
End With
maxR = lr1
maxC = lc1
If maxR < lr2 Then maxR = lr2
If maxC < lc2 Then maxC = lc2
DiffCount = 0
For c = 1 To maxC
Application.StatusBar = "Comparing cells " & Format(c / maxC, "0 %") & "..."
For r = 1 To maxR
cf1 = ""
cf2 = ""
On Error Resume Next
cf1 = ws1.Cells(r, c).FormulaLocal
cf2 = ws2.Cells(r, c).FormulaLocal
On Error GoTo 0
If cf1 <> cf2 Then
DiffCount = DiffCount + 1
Cells(r, c).Formula = "'" & cf1 & " <> " & cf2
End If
Next r
Next c
Application.StatusBar = "Formatting the report..."
With Range(Cells(1, 1), Cells(maxR, maxC))
.Interior.ColorIndex = 19
With .Borders(xlEdgeTop)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeRight)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
On Error Resume Next
With .Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
With .Borders(xlInsideVertical)
.LineStyle = xlContinuous
.Weight = xlHairline
End With
On Error GoTo 0
End With
Columns("A:IV").ColumnWidth = 20
rptWB.Saved = True
If DiffCount = 0 Then
rptWB.Close False
End If
Set rptWB = Nothing
Application.StatusBar = False
Application.ScreenUpdating = True
MsgBox DiffCount & " cells contain different formulas!", vbInformation, _
"Compare " & ws1.Name & " with " & ws2.Name
End Sub
In the worksheet that will be updated, convert each row to a string and save it to a dictionary. Then, in the worksheet you're updating from, loop through all the rows, get their string rep and see if it exists in the dictionary. If it doesn't, then add them.
Here's an example code that gets a string from a rows values
Sub getRowAsString()
Dim cell As Object
Dim sheet As Worksheet
Dim str As String
Dim arr() As Variant
Dim arr2() As Variant
Dim printCol As Integer
Set sheet = ActiveSheet
printCol = sheet.UsedRange.Columns.Count + 1
For Each cell In sheet.UsedRange.Rows
arr = cell.Value2
ReDim arr2(LBound(arr, 2) To UBound(arr, 2))
For i = LBound(arr, 2) To UBound(arr, 2)
arr2(i) = arr(1, i)
Next i
str = Join(arr2, ", ")
ActiveSheet.Cells(cell.Row, printCol).value = str
Next cell
End Sub
Here's an example of using a dictionary:
Sub createDictionary()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Key = "hello"
value = "world"
dict.Add Key, value
MsgBox "key exists: " & dict.exists(Key) & vbNewLine & "value: " & dict(Key)
End Sub
If the string representation of a row is too large, you can save a hashed value of it into the dictionary to make it more manageable. Here is a post that gives VBA code for hashing a string

Resources