10

I have a subroutine that deletes rows in a range containing around 1000 rows. Rows are deleted on a critera. The code below works.

However, when I run the macro I usually have to run it 4 times before all rows containing the removal criteria are removed.

I guess this is because the for loop misses its index when a row suddenly dissapears when deleting a row.

My first code looks like this.

Set StatusRange = Range("B2", Range("B2").End(xlDown))

For Each StatusCell In StatusRange
    If StatusCell = "FG" Then
        StatusCell.EntireRow.Delete
    ElseIf StatusCell = "QC" Then
        StatusCell.EntireRow.Delete
    ElseIf StatusCell = "CS" Then
        StatusCell.EntireRow.Delete
    Else
    End If
Next StatusCell

When I try to update the range each loop, it still doesn't work.

Set StatusRange = Range("B2", Range("B2").End(xlDown))

For Each StatusCell In StatusRange
    If StatusCell = "FG" Then
        StatusCell.EntireRow.Delete
    ElseIf StatusCell = "QC" Then
        StatusCell.EntireRow.Delete
    ElseIf StatusCell = "CS" Then
        StatusCell.EntireRow.Delete
    Else
    End If
                
    Set StatusRange = Range("B2", Range("B2").End(xlDown))
Next StatusCell
        

Is there anyone who know a sloution to this?

6
  • 3
    Work from teh bottom up. If you delete a row, everything moves up and yuou skip that row on teh next iteration. Commented Apr 17, 2017 at 15:11
  • 1
    This is almost certainly a duplicate question and I'll mark it as such when I find the suitable duplicate. Whenever you delete items from a collection, you must delete from Rows.Count to 1 Step -1 otherwise you will skip rows. Commented Apr 17, 2017 at 15:12
  • Go ahead and dupe this @DavidZemens - I've added some value by showing the Case statement for multiple comparisons but this is still a dupe. Commented Apr 17, 2017 at 15:16
  • Here's a good explanation: stackoverflow.com/questions/23689196/… Commented Apr 17, 2017 at 15:17
  • And another with an accepted answer which actually appears to be VB.NET code but the principle is the same: stackoverflow.com/questions/10315120/… Commented Apr 17, 2017 at 15:17

4 Answers 4

16

Work from the bottom up. If you delete a row, everything moves up and you skip that row on the next iteration.

Here is the 'guts' of the code to work up from the bottom.

With Worksheets("Sheet1")
    For rw = .Cells(.Rows.Count, "B").End(xlUp).Row To 2 Step -1
        Select Case UCase(.Cells(rw, "B").Value2)
            Case "FG", "QC", "CS"
                .Rows(rw).EntireRow.Delete
        End Select
    Next rw
End With
Sign up to request clarification or add additional context in comments.

2 Comments

I was writing literal same answer (complete with Case statement) haha :D
Damn, I was just finished typing and posting almost the exact same code ;)
4

Since there's no Reverse loop for For Each you need to use a slightly different approach.

Also, your code with multiple Ifs and OR is "screaming for the use of Select Case.

Dim StatusRange As Range
Dim i As Long

Set StatusRange = Range("B2", Range("B2").End(xlDown))

' loop backward when deleting Ranges, Rows, Cells
For i = StatusRange.Rows.Count To 1 Step -1
    Select Case StatusRange(i, 1).Value
        Case "FG", "QC", "CS"
            StatusRange(i, 1).EntireRow.Delete
        Case Else ' for the future if you need it

    End Select
Next i

Comments

4

Wait til the For Each loops ends before deleting the rows:

Option Explicit

Sub mySub()

    Dim myRange As Range
    Set myRange = ActiveSheet.UsedRange

    Dim myCell As Range
    Dim myTrash As Range

    For Each myCell In myRange
        Select Case myCell.Value
            Case "my value"
                If myTrash Is Nothing _
                    Then
                        Set myTrash = myCell.EntireRow
                    Else
                        Set myTrash = Union(myTrash, myCell.EntireRow)
                End If
        End Select
    Next

    If Not myTrash Is Nothing Then myTrash.Delete

End Sub

1 Comment

For me this was actually the best solution. I just added If rngToBeDeleted Is Nothing Then Set rngToBeDeleted = xlRow.EntireRow before the Union() to avoid an error when the range is not set initially.
-1
start_over: s = 0  
    for each cell  
        if cell = "FG" then   
           s = 1            'Marked to start over if ifstmt was true
           [other code]
        end if  
    next cell  
   If s = 1 Then GoTo start_over  

'your code:

Set StatusRange = Range("B2", Range("B2").End(xlDown))  
    start_over: s = 0  
    For Each StatusCell In StatusRange  
        If StatusCell = "FG" or StatusCell = "QC" or StatusCell = "CS" Then StatusCell.EntireRow.Delete: s=1  
    Next StatusCell  
   If s = 1 Then GoTo start_over

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.