Deleting rows in VBA based on a list - vba

I have two tabs in my spreadsheets (Report and Holidays). In column A of Holidays tab there is a list of dates (updated manually) which I want to exclude from Report tab (column E contains dates).
I have found a code which does what is needed but takes some time when the number of rows is around 100-200k:
Sub Holidays()
Application.DisplayAlerts = False
Dim d As Object, e, rws&, cls&, i&, j&
Set d = CreateObject("scripting.dictionary")
For Each e In Sheets("Holidays").Range("A1").CurrentRegion
d(e.Value) = 1
Next e
Sheets("Report").Activate
rws = Cells.Find("*", After:=[a1], SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
cls = Cells.Find("*", After:=[a1], SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
For i = rws To 1 Step -1
For j = 1 To cls
If d(Range("A1").Resize(rws, cls)(i, j).Value) = 1 Then _
Cells.Rows(i).Delete: Exit For
Next j, i
Application.DisplayAlerts = True
End Sub
Is there a way to speed up that macro? Ideally it should take only several seconds to run.
Thank you in advance for your help.

This should remove about 10 K rows out of 200 K, in less than 30 seconds
Code bellow assumes that UsedRange on both sheets starts in A1, and
Sheet Holidays contains only column A (in contiguous rows)
Sheet Report contains dates to be removed in column E (in contiguous rows)
Dates on both sheets are formatted as "m/d/yyyy"
Option Explicit
Public Sub RemoveHolidaysFromReportFilterUnion()
Const WS_NAME = "Report"
Dim wsH As Worksheet: Set wsH = ThisWorkbook.Worksheets("Holidays")
Dim wsR As Worksheet: Set wsR = ThisWorkbook.Worksheets(WS_NAME)
Dim del As Range, wsNew As Worksheet
Application.ScreenUpdating = False
Set del = GetRowsToDelete(wsH, wsR)
If del.Cells.Count > 1 Then
del.EntireRow.Hidden = True
Set wsNew = ThisWorkbook.Worksheets.Add(After:=wsR)
wsR.UsedRange.SpecialCells(xlCellTypeVisible).EntireRow.Copy
With wsNew.Cells(1)
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteAll
.Select
End With
Application.DisplayAlerts = False
wsR.Delete
Application.DisplayAlerts = True
wsNew.Name = WS_NAME
End If
Application.ScreenUpdating = True
End Sub
Private Function GetRowsToDelete(ByRef wsH As Worksheet, ByRef wsR As Worksheet) As Range
Const HOLIDAYS_COL = "A"
Const REPORT_COL = "E"
Dim arr As Variant, i As Long, itm As Variant
ReDim arr(1 To wsH.UsedRange.Rows.Count - 1)
i = 1
For Each itm In wsH.UsedRange.Columns(HOLIDAYS_COL).Offset(1).Cells
If Len(itm) > 0 Then
arr(i) = itm.Text 'Create AutoFilter Array (dates as strings)
i = i + 1
End If
Next
Dim ur As Range, del As Range, lr As Long, fc As Range
With wsR.UsedRange
Set ur = .Resize(.Rows.Count - 1, 1).Offset(1)
Set del = wsR.Cells(.Rows.Count + 1, REPORT_COL)
End With
lr = wsR.UsedRange.Rows.Count
Set fc = wsR.Range(wsR.Cells(1, REPORT_COL), wsR.Cells(lr, REPORT_COL))
fc.AutoFilter Field:=1, Criteria1:=arr, Operator:=xlFilterValues
If fc.SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
Set del = Union(del, ur.SpecialCells(xlCellTypeVisible))
End If
fc.AutoFilter
Set GetRowsToDelete = del
End Function
Performance - Removed about 5K rows out of a total of 100K
Sheet Report - Rows: 100,011, Cols: 11 (Rows Left: 94,805 - Deleted: 5,206)
Sheet Holidays - Rows: 20, Cols: 1
Initial Sub - Holidays() - Time: 112.625 sec
RemoveHolidaysFromReportFilterUnion() - Time: 10.512 sec
Test Data
Holidays
Report - Before
Report - After

I would suggest using arrays.
Populate an Array for the list of dates. example:
arr = Array("Alpha","Bravo","Charlie")
Filter the report based on Criteria.
Sheet17.Range("E1").AutoFilter Field:=5, Criteria1:=arr, Operator:=xlFilterValues
Once the sheet is filtered, create a range of selected visible cells
set myrange = range("A1:F" &_
Cells(Rows.Count,"A").end(xlup).row).SpecialCells(xlCellTypeVisible)
Delele the Range using Range(“YourRange”).EntireRow.Delete
This will delete the range in 4 operations instead of looping through every row in the range based on the condition.
Hope that helps!

Related

VBA script causes Excel to not respond after 15 loops

I am running a script to find and delete rows that contain data from after 2018. I am searching through around 650000 rows. Every time I run the script after 5 seconds my cursor becomes the spinning circle and the excel program becomes unresponsive. Here is the code I am using.
Option Explicit
Option Base 1 'row and column index will match array index
Sub removeWrongYear()
Dim i As Long, yearA As Long, rowsCnt As Long
Dim rowsToDelete As Range
Dim vData As Variant
With ActiveSheet
'1st to 635475 row, 20th column
vData = Range(.Cells(1, 20), .Cells(635475, 20))
For i = UBound(vData) To 2 Step -1
If Val(Right(vData(i,1),2)) > 17 Then
Debug.Print Val(Right(vData(i,1),2))
rowsCnt = rowsCnt + 1
If rowsCnt > 1 Then
Set rowsToDelete = Union(rowsToDelete, .Rows(i))
ElseIf rowsCnt = 1 Then
Set rowsToDelete = .Rows(i)
End If
End If
Next i
End With
If rowsCnt > 0 Then
Application.ScreenUpdating = False
rowsToDelete.EntireRow.Delete
Application.ScreenUpdating = True
End If
End Sub
Every time I run the script after 5 seconds my cursor becomes the spinning circle and the excel program becomes unresponsive.
That's normal. VBA is running on the single available UI thread, the same one Excel runs on. While it's busy running your loop, it's not able to respond to other stimuli, and tells you that by putting "(not responding)" in the title bar, until it completes the work and is able to resume doing everything else it needs to do (i.e. listen for mouse & keyboard messages, etc.).
You could add a little DoEvents in the body of that loop to allow Excel to breathe and process pending messages between iterations, but then there's a catch: first, your code will take even longer to complete, and second, if the user is able to select/activate another sheet in the middle of that loop, then this unqualified Range call:
vData = Range(.Cells(1, 20), .Cells(635475, 20))
...will be the source of a run-time error 1004, since you can't do Sheet1.Range(Sheet2.Cells(1,20), Sheet2.Cells(635475,20)) and expect Excel to know what to do with that (assuming Sheet2 was active when the loop started, and the user activated Sheet1 in the middle of it).
This answer provides what appears to be the most efficient approach to conditionally deleting lines when a lot of rows are involved. If you can, add a helper column to calculate your criteria (e.g. make it return TRUE for rows to keep and FALSE for rows to delete), then use Worksheet.Replace and Worksheet.SpecialCells to perform the filtering and deletion:
.Columns("Z:Z").Replace What:=False, _
Replacement:="", _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
MatchCase:=False, _
SearchFormat:=False, _
ReplaceFormat:=False
.Columns("Z:Z").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Then you don't need a loop, and it might actually complete before you get to count to 5 seconds.
Other than that, long-running operations are just that: long-running operations. Own it:
Application.StatusBar = "Please wait..."
Application.Cursor = xlWait
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
'..code..
Application.Cursor = xlDefault
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.StatusBar = False
This seems pretty quick. It puts results in U1 and down so you'd probably want to amend that. This extracts the values you want into a second array.
Sub removeWrongYear()
Dim i As Long, vData As Variant, v2(), j As Long
vData = Range(Cells(1, 20), Cells(635475, 20))
ReDim v2(1 To UBound(vData, 1), 1 To 1)
For i = UBound(vData) To 2 Step -1
If Val(Right(vData(i, 1), 2)) <= 17 Then
j = j + 1
v2(j, 1) = vData(i, 1)
End If
Next i
Range("U1").Resize(j, 1) = v2
End Sub
This uses an AutoFilter - the more rows to delete, the faster it gets
Rows: 1,048,575 (Deleted: 524,286), Cols: 21 (70 Mb xlsb file)
Time: 6.90 sec, 7.49 sec, 7.21 sec (3 tests)
Test data shown in images bellow
How it works
It generates a temporary helper column with formula "=RIGHT(T1, 2)" (first empty column)
Applies a filter for the years to keep ("<18") in the temp column
Copies all visible rows to a new sheet (not including the temp column)
Removes the initial sheet
Renames the new sheet to the initial sheet name
Option Explicit
Public Sub RemoveYearsAfter18()
Dim ws As Worksheet, wsName As String, lr As Long, lc As Long
Dim ur As Range, filterCol As Range, newWs As Worksheet
Set ws = Sheet1 'Or: Set ws = ThisWorkbook.Worksheets("Sheet1")
wsName = ws.Name
lr = ws.Cells(ws.Rows.Count, "T").End(xlUp).Row 'Last Row in col T (or 635475)
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column 'Last Col in row 1
Set ur = ws.Range(ws.Cells(1, 1), ws.Cells(lr, lc))
Set filterCol = ws.Range(ws.Cells(1, lc + 1), ws.Cells(lr, lc + 1)) 'Exclude Headers
OptimizeApp True
Set newWs = ThisWorkbook.Worksheets.Add(After:=ws) 'Add new sheet
With filterCol
.Formula = "=RIGHT(T1, 2)"
.Cells(1) = "FilterCol" 'Column header
.Value2 = .Value2 'Convert formulas to values for filter
End With
filterCol.AutoFilter Field:=1, Criteria1:="<18" 'Reverse filter
ur.Copy 'Copy visible data
With newWs.Cells
.PasteSpecial xlPasteColumnWidths
.PasteSpecial xlPasteAll 'Paste data on new sheet
.Cells(1).Select
End With
ws.Delete 'Delete old sheet
newWs.Name = wsName
OptimizeApp False
End Sub
Private Sub OptimizeApp(ByVal speedUp As Boolean)
Application.Calculation = IIf(speedUp, xlCalculationManual, xlCalculationAutomatic)
Application.ScreenUpdating = Not speedUp
Application.DisplayAlerts = Not speedUp
Application.EnableEvents = Not speedUp
End Sub
Before
After
This code process 635475 Rows x 20 Columns in 12.48 seconds on my fast computer and 33.32 seconds on my old computer (0.84 and 2.06 seconds for 38k x 20).
Option Explicit
Sub removeWrongYear2()
Const DATE_COLUMN = 20
Dim StartTime As Double: StartTime = Timer
Dim data() As Variant, results() As Variant
Dim c As Long, r As Long, r2 As Long
With ActiveSheet
data = .UsedRange.Value
ReDim results(1 To UBound(data), 1 To UBound(data, 2))
For r = 2 To UBound(data)
If Val(Right(data(r, DATE_COLUMN), 2)) <= 17 Then
r2 = r2 + 1
For c = 1 To UBound(data, 2)
results(r2, c) = data(r, c)
Next
End If
Next
If r2 > 0 Then
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
.UsedRange.Offset(1).Value = results
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End If
End With
Debug.Print Round(Timer - StartTime, 2)
End Sub
Sub Setup()
Dim data, r, c As Long
Const LASTROW = 635475
Cells.Clear
data = Range(Cells(1, 1), Cells(LASTROW, 20)).Value
For r = 1 To UBound(data)
For c = 1 To 19
data(r, c) = Int((LASTROW * Rnd) + 100)
Next
data(r, 20) = Int((10 * Rnd) + 10)
Next
Application.ScreenUpdating = False
Range(Cells(1, 1), Cells(LASTROW, 20)).Value = data
Application.ScreenUpdating = True
End Sub
Sort() & AutoFilter() are always a good pair:
Sub nn()
Dim sortRng As Range
With ActiveSheet.UsedRange ' reference all data in active sheet
With .Offset(, .Columns.Count).Resize(, 1) ' get a helper column right outside data
.Formula = "=ROW()" ' fill it with sequential numbers from top to down
.Value = .Value ' get rid of formulas
Set sortRng = .Cells ' store the helper range
End With
With .Resize(, .Columns.Count + 1) ' consider data and the helper range
.Sort key1:=.Cells(1, 20), order1:=xlAscending, Header:=xlNo ' sort it by data in column 20
.AutoFilter Field:=20, Criteria1:=">=01/01/2018" ' filter it for data greater than 2017
.Resize(.Rows.Count - 1).Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Delete ' delete filtered data
.Parent.AutoFilterMode = False ' remove filter
.Sort key1:=sortRng(1, 1), order1:=xlAscending, Header:=xlNo ' sort things back by means of helper column
.Columns(.Columns.Count).ClearContents ' clear helper column
End With
End With
End Sub
in my test a 768k row by 21 columns data took 11 seconds

How to create a multiple criteria advance filter in VBA?

I'm trying to create an advanced filter for the below table but the code below is just hiding the cells. It's working but my problem with it is if i filter something and then I drag to fill status or any other cells it will override the cells in between for example in filter mode I have 2 rows one is 1st row and the other one is at row 20 if I drag to fill status it will replace the status of all cells in between 1 and 20 and don't know how to work it out, i know this happens because I'm hiding the cells and not actually filtering them.
Any help will be much appreciated.
[Data Table][1]
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
Application.EnableEvents = False
r1 = Target.Row
c1 = Target.Column
If r1 <> 3 Then GoTo ending:
If ActiveSheet.Cells(1, c1) = "" Then GoTo ending:
Dim LC As Long
With ActiveSheet
LC = .Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
End With
ActiveSheet.Range("4:10000").Select
Selection.EntireRow.Hidden = False
LR = Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
For r = 5 To LR
For c = 1 To LC
If ActiveSheet.Cells(2, c) = "" Or ActiveSheet.Cells(3, c) = "" Then GoTo nextc:
If ActiveSheet.Cells(2, c) = "exact" And UCase(ActiveSheet.Cells(r, c)) <> UCase(ActiveSheet.Cells(3, c)) Then ActiveSheet.Rows(r).EntireRow.Hidden = True: GoTo nextr:
If Cells(2, c) = "exact" Then GoTo nextc:
j = InStr(1, UCase(ActiveSheet.Cells(r, c)), UCase(ActiveSheet.Cells(3, c)))
If ActiveSheet.Cells(2, c) = "partial" And j = 0 Then ActiveSheet.Rows(r).EntireRow.Hidden = True: GoTo nextr:
nextc:
Next c
nextr:
Next r
ending:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
The below code will be the answer to the question on how to create an advanced search based on multiple criteria on what the user selects in the table.
I will need a little bit of help with how to check if the user selected by mistake an empty cell I will need to make excel ignore filtering the blank cell. Also, I will need to make excel first to check if the yellow cells A3 to T3 has data in and if it has and i press the filter button will filter by the range A3:T3 and ignore the current user selection if there is no data in range A3:T3 will filter by the user selection and in the range A3:T3, if it has data will only filter by data cell that has data in them and ignore empty ones.
Sub advancedMultipleCriteriaFilter()
Dim cellRng As Range, tableObject As Range, subSelection As Range
Dim filterCriteria() As String, filterFields() As Integer
Dim i As Integer
If Selection.Rows.Count > 1 Then
MsgBox "Cannot apply filters to multiple rows within the same column. Please make another selection and try again.", vbInformation, "Selection Error!"
Exit Sub
End If
Application.ScreenUpdating = False
i = 1
ReDim filterCriteria(1 To Selection.Cells.Count) As String
ReDim filterFields(1 To Selection.Cells.Count) As Integer
Set tableObject = Selection.CurrentRegion
For Each subSelection In Selection.Areas
For Each cellRng In subSelection
filterCriteria(i) = cellRng.Text
filterFields(i) = cellRng.Column - tableObject.Cells(1, 1).Column + 1
i = i + 1
Next cellRng
Next subSelection
With tableObject
For i = 1 To UBound(filterCriteria)
.AutoFilter field:=filterFields(i), Criteria1:=filterCriteria(i)
Next i
End With
Set tableObject = Nothing
Application.ScreenUpdating = True
End Sub
Sub resetFilters()
Dim sht As Worksheet
Dim LastRow As Range
Application.ScreenUpdating = False
On Error Resume Next
If ActiveSheet.FilterMode Then
ActiveSheet.ShowAllData
End If
Range("A3:T3").ClearContents
Application.ScreenUpdating = True
Call GetLastRow
End Sub
Private Sub GetLastRow()
'Step 1: Declare Your Variables.
Dim LastRow As Long
'Step 2: Capture the last used row number.
LastRow = Cells(Rows.Count, 8).End(xlUp).Row
'Step 3: Select the next row down
Cells(LastRow, 8).Offset(1, 0).Select
End Sub

Delete duplicate row in excel by comparing a column

Given below is an example of data i'm working with
As you can see it has duplicate entries.(actual database is 30000 entries)
I would like to find a method on how I can remove duplicate rows base on the corresponding column where percentage is listed.
The method should compare duplicate row percentages and choose the highest one and discard the other
This the question(initial):
I hope its clear with this output.
This is the result, I want
Any help would be appreciated!
Try this. It will (should) sort the data by the email and percentage column, then remove duplicates leaving the highest percentage intact.
With ActiveSheet
.Range("A1:B" & Cells(Rows.Count, "A").End(xlUp).Row).Sort _
Key1:=Range("A1"), Order1:=xlDescending, Header:=xlYes, KEY2:=Range("B1"), Order2:=xlDescending, Header:=xlYes
.Range("A1:B" & Cells(Rows.Count, "A").End(xlUp).Row).RemoveDuplicates Columns:=Array(1), Header:=xlYes
End With
If you only need this method once you can do it manually.
Step 1: Sort by percentage descending.
Step 2: Use the 'Remove Duplicates' function on the data ribbon. Use it on the 'Email' column only.
1- clic DATA tab
2-select your data and clic Remove Duplicates
3-Select corresponding columns and clic OK.
Here is an altered version of the following post:
Delete all rows if duplicate in excel - VBA
Sub remDup2()
Dim rng As Range, dupRng As Range, lastrow As Long, ws As Worksheet
Dim col As Long, col2 As Long, offset As Long, deletecurrent As Boolean
'Disable all the stuff that is slowing down
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Define your worksheet here
Set ws = Worksheets(1)
'Define your column and row offset here
col = 1 'Column with E-Mail
col2 = 2 'Column with percentage
offset = 1 'Startrow with entries
'Find first empty row
Set rng = ws.Cells(offset + 1, col)
lastrow = rng.EntireColumn.Find( _
What:="", After:=ws.Cells(offset + 1, col)).Row - 1
'Loop through list
While (rng.Row < lastrow)
Do
Set dupRng = ws.Range(ws.Cells(rng.Row + 1, col), ws.Cells(lastrow, col)).Find( _
What:=rng, LookAt:=xlWhole)
If (Not (dupRng Is Nothing)) Then
If (ws.Cells(rng.Row, col2) > ws.Cells(dupRng.Row, col2)) Then
dupRng.EntireRow.Delete
lastrow = lastrow - 1
Else
deletecurrent = True
Exit Do
End If
If (lastrow = rng.Row) Then Exit Do
Else
Exit Do
End If
Loop
Set rng = rng.offset(1, 0)
'Delete current row
If (deletecurrent) Then
rng.offset(-1, 0).EntireRow.Delete
lastrow = lastrow - 1
End If
deletecurrent = False
Wend
'Enable stuff again
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub

Find Specific Value, Delete Corresponding Range. Macro agonozingly slow

In my spreadsheet I have something close to 2,000 rows. I need to search through these rows, find a specific date (current date), and then delete a corresponding range. It however runs very very slowly. Any suggestions about how I can make it run faster? I was thinking that maybe I could organize my rows based on the date (current date will always be the oldest and therefore be on the top) and then delete all of the rows at once with a Range(XX:XX").Delete. But I don't know how to find where the last row with Currentdate would be as it is going to be constantly changing.
Sub ChangeandDelete
MudaDataLCA
DeleteDateLCA
End Sub
Sub MudaDataLCA()
'===Muda Data Atual ABERTURA===
Dim Affected As Workbook
Dim Dados As Worksheet
Dim LastRow As Long
Set Affected = Workbooks("Controle de Lastro LCA_FEC - Test")
Set Dados = Affected.Sheets("DADOS")
Dados.Activate
Dim CurrentDate As Date
CurrentDate = Range("AH2") + 1
Range("AH2") = CurrentDate
End Sub
Sub DeleteDateLCA()
Dim Affected As Workbook
Dim Dados As Worksheet
Dim LastRow As Long
Set Affected = Workbooks("Controle de Lastro LCA_FEC - Test")
Set Dados = Affected.Sheets("DADOS")
Dados.Activate
LastRow = Dados.Cells(Rows.Count, "P").End(xlUp).Row
For i = 5 To LastRow
Do While Range("S" & i).Value = Range("AH2")
Range("P" & i & ":AG" & i).Delete
Loop
Next i
End Sub
This method of filtering for the updated date in AH2 should speed the process up significantly.
Sub ChangeandDelete()
Dim fr As Long, lr As Long, fString As String
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
With Workbooks("Controle de Lastro LCA_FEC - Test").Sheets("DADOS")
.Range("AH2") = CDate(.Range("AH2").Value + 1)
fr = 4: lr = .Cells(Rows.Count, "P").End(xlUp).Row
fString = Chr(61) & Format(.Range("AH2").Value, .Range("P5").NumberFormat)
With .Range(.Cells(fr, "P"), .Cells(lr, "P"))
.AutoFilter
.AutoFilter Field:=1, Criteria1:=fString
If CBool(Application.Subtotal(102, .Columns(1)) + IsNumeric(.Cells(1, 1).Value2)) Then
With .Offset(1, 0).Resize(.Rows.Count - 1, .Columns.Count)
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
Debug.Print Application.Count(.Columns(1))
End If
.AutoFilter
End With
End With
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
I've assumed that at least part of the slowdown was formula recalculation every time a row was deleted and this would indicate automatic calculation. I've turned off automatic calculation and restored it once the process is complete. There are other methods of storing the current state of the workbook/worksheet calculation, turning calculation off, then restoring the original state.
So I've got two answers. I put in 39000 rows of data and did it with 7500 rows that would meet the criteria for deleting - so I could test the time (64bit windows 7)
Loops can be super slow but I'll write this one first because it's closest to your code:
Sub DeleteIT()
Dim deleteRange As Range
Dim deleteValue As Date
Dim lastRow As Long
Set affected = ThisWorkbook
Set dados = affected.Sheets("DADOS")
Dim CTtimer As CTimer
'Set CTtimer = New CTimer
'Dados.Activate
Application.ScreenUpdating = False
deleteValue = dados.Range("AH2")
lastRow = dados.Range("S" & dados.Rows.Count).End(xlUp).Row
'CTtimer.StartCounter
Do
Set deleteRange = Range("S5:S" & lastRow).Find(what:=deleteValue, LookIn:=xlValues, _
lookat:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not deleteRange Is Nothing Then deleteRange.Range(Cells(1, 1), Cells(1, 18)).Offset(0, -3).Delete
Loop While Not deleteRange Is Nothing
'MsgBox CTtimer.TimeElapsed
Application.ScreenUpdating = True
End Sub
I got throught about 500 rows and 150 deletes of matching records in 4 mins with the above code. I did a code break and stopped because nobody should have to deal with that haha..
My other idea(below) is more along the lines of your sort idea, this way only took about 25 seconds to do 30500 deletes from 31500 rows.
Sub aReader()
Dim affected As Workbook
Dim SheetName As String
Dim deleteValue As Date
Dim population As Range
Dim lastRow As Long
Dim x As Long
'Dim CTtimer As CTimer
'Set CTtimer = New CTimer
Set affected = ThisWorkbook
Application.ScreenUpdating = False
SheetName = "DADOS"
deleteValue = affected.Worksheets(SheetName).Range("AH2")
Set population = Worksheets(SheetName).Range("P5", Sheets(SheetName).Range("P5").End(xlDown))
'CTtimer.StartCounter
For x = 1 To population.Count
If population.Cells(x, 4).Value = deleteValue Then Range(population.Cells(x, 1), population.Cells(x, 18)).Value = ""
Next x
Range("P5:AG" & (population.Count + 4)).Sort key1:=Range("S5:S" & population.Count + 4), _
order1:=xlAscending, Header:=xlNo
Application.ScreenUpdating = True
'MsgBox CTtimer.TimeElapsed
End Sub

Run-time error 1004 Application-defined or object defined error

I have looked through the other posts about this and have tried adapted the strategies that were recommend by using Set ActiveWorkbook and Set Active Worksheet and I still get the same error. I hope another set of eyes can help out as I am still very new to VBA and I am not all that comfortable with it yet.
Basically the idea is to copy the cells from column f to column j as values as long as the cells of F do not match the cells of J. I get the row count of column E and use that as my count in the for loop.
Code is here:
Private Sub CalculateRewards_Click()
CopyPaste
End Sub
Sub CopyPaste()
Dim n As Integer
Dim i As Integer
n = Sheets("Calculate").Range("E:E").Cells.SpecialCells(xlCellTypeConstants).Count
i = n
For Counter = 1 To n
Set curCell = Sheets("Calculate").Range("F2:F" &i)
If "$F" &i <> "$J" &i Then
Sheets("Calculate").Range("$F:$F" &i).Copy
Sheets("Calculate").Range("$J:$J" &i).PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
End If
i = i + 1
Next Counter
End Sub
Thanks for the help
Also Edit:
Link to Excel Sheet that has a before page, after first transaction sheet ,and a after second transaction sheet: https://www.dropbox.com/s/n2mn0zyrtoscjin/Rewards.xlsm
CHange this:
Set curCell = Sheets("Calculate").Range("F2:F" &i)
If "$F" &i <> "$J" &i Then
Sheets("Calculate").Range("$F:$F" &i).Copy
Sheets("Calculate").Range("$J:$J" &i).PasteSpecial (xlPasteValues)
Application.CutCopyMode = False
End If
To this:
Set curCell = Sheets("Calculate").Range("F2:F" & i)
If curCell <> Sheets("Calculate").Range("$J" & i) Then
Sheets("Calculate").Range("$J:$J" &i).Value = curCell.Value
End If
May need to do some more teaking as I notice you're working with SpecialCells which essentially filters the range, so iterating For i = 1 to n... probably does not work. Maybe something like:
Dim rngCalc as Range
Set rngCalc = Sheets("Calculate").Range("E:E").Cells.SpecialCells(xlCellTypeConstants)
For each curCell in rngCalc.Cells
If curCell <> curCell.Offset(0, 4) Then
curCell.Offset(0, 4).Value = curCell.Value
End If
Next
EDIT: this sub will calculate the points for the last transaction (identified as the furthest-right column containing transactions) and write them down in column C.
Option Explicit
Sub UpdateCurrentPurchase()
Dim CalcSheet As Worksheet
Dim LastTransRange As Range, TargetRange As Range
Dim LastTransCol As Long, LastTransRow As Long
Dim PurchaseArray() As Variant
Dim Points As Long, Index As Long
'set references up-front
Set CalcSheet = ThisWorkbook.Worksheets("Calculate")
With CalcSheet
LastTransCol = .Cells(2, .Columns.Count).End(xlToLeft).Column '<~ find the last column
LastTransRow = .Cells(.Rows.Count, LastTransCol).End(xlUp).Row
Set LastTransRange = .Range(.Cells(2, LastTransCol), .Cells(LastTransRow, LastTransCol))
Set TargetRange = .Range(.Cells(2, 6), .Cells(LastTransRow, 6)) '<~ column F is the Current Purchase Col
LastTransRange.Copy Destination:=TargetRange '<~ copy last transactions to Current Purchase Col
End With
'pull purchases into a variant array
PurchaseArray = TargetRange
'calculate points
For Index = 1 To LastTransRow
Points = Int(PurchaseArray(Index, 1) / 10) '<~ calculate points
CalcSheet.Cells(Index + 1, 3) = Points '<~ write out the points amount in col C
Next Index
End Sub
ORIGINAL RESPONSE: I think the below will get you where you're going. That being said, it seems like simply overwriting column J with column F (as values) might be the fastest way to an acceptable answer, so if that's the case we can re-work this code to be much quicker using Range objects.
Option Explicit
Private Sub CalculateRewards_Click()
CopyPaste
End Sub
Sub CopyPaste()
Dim LastRow As Long, Counter As Long
Dim cSheet As Worksheet '<~ add a worksheet reference to save some typing
'set references up front
Set cSheet = ThisWorkbook.Worksheets("Calculate")
With cSheet
LastRow = .Range("E" & .Rows.Count).End(xlUp).Row '<~ set loop boundary
'loop that compares the value in column 6 (F) to the value in
'column 10 (J) and writes the value from F to J if they are not equal
For Counter = 1 To LastRow
If .Cells(Counter, 6).Value <> .Cells(Counter, 10).Value Then
.Cells(Counter, 10) = .Cells(Counter, 6)
End If
Next Counter
End With
End Sub