John
John

Reputation: 43

Deleting rows in VBA based on a list

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.

Upvotes: 3

Views: 1285

Answers (2)

Akshit A.
Akshit A.

Reputation: 27

I would suggest using arrays.

  1. Populate an Array for the list of dates. example:

    arr = Array("Alpha","Bravo","Charlie")

  2. Filter the report based on Criteria.

Sheet17.Range("E1").AutoFilter Field:=5, Criteria1:=arr, Operator:=xlFilterValues

  1. 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)
  1. 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!

Upvotes: 0

paul bica
paul bica

Reputation: 10715

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

Holidays


Report - Before

ReportBefore

Report - After

ReportAfter

Upvotes: 1

Related Questions