Home > OS >  For Loop To Delete Row with Condition at Certain Cell Value
For Loop To Delete Row with Condition at Certain Cell Value

Time:03-28

I want to delete entire row when the cell value at column 7 is "No", It didn't work well because some cell missed to delete even the cell value is "No". The macro need to run more than once to completelly delete the row which content "No" value

Sub Delete_Rows()
    Dim StartRow As Integer
    Dim LastRow As Integer
    Dim ColNum As Integer
    Dim CellVal As String

    ColNum = 7
    StartRow = 8

    Worksheets("Email_Status_Bonus").Activate
    LastRow = Cells(Rows.Count, ColNum).End(xlUp).Row
    For i = StartRow To LastRow
        CellVal = Cells(i, ColNum).Value
        If CellVal = "No" Then
            Cells(i, ColNum).EntireRow.Delete
        End If
        StartRow = StartRow   1
    Next i

End Sub

Before running the macro After running the macro, some row didn't delete even the cell value is "No"

CodePudding user response:

Delete Data Rows Using AutoFilter

  • Not entire rows!
  • It is assumed that there is at least one empty column to the right and at least one empty row below the data i.e. possible data to the right will stay intact, while rows below will shift up as many rows as data rows were deleted.
  • Possible data to the left or above the first cell will be ignored.
Option Explicit

Sub DeleteDataRows()
    
    Application.ScreenUpdating = False
    
    Dim dfrg As Range ' Data Filtered Range
    
    With ThisWorkbook.Worksheets("Email_Status_Bonus")
        ' Remove possible initial filter.
        If .FilterMode Then .ShowAllData
        ' Reference the range (has headers).
        Dim fCell As Range: Set fCell = .Range("A7")
        Dim rg As Range
        With fCell.CurrentRegion
            Set rg = fCell.Resize(.Row   .Rows.Count _
                - fCell.Row, .Column   .Columns.Count - fCell.Column)
        End With
        ' Reference the data range (no headers).
        Dim drg As Range: Set drg = rg.Resize(rg.Rows.Count - 1).Offset(1)
        ' Filter the range (has headers).
        rg.AutoFilter 7, "No"
        ' Attempt to reference the data filtered range (no headers).
        On Error Resume Next
            Set dfrg = drg.SpecialCells(xlCellTypeVisible)
        On Error GoTo 0
        ' Remove the filter.
        .AutoFilterMode = False
    End With
    
    Dim IsSuccess As Boolean ' indicates if any rows were deleted
    
    ' Delete referenced filtered rows (if any).
    If Not dfrg Is Nothing Then
        dfrg.Delete xlShiftUp
        IsSuccess = True
    End If
    
    ' Save the workbook.
    'Thisworkbook.Save
    
    Application.ScreenUpdating = True
    
    ' Inform.
    If IsSuccess Then
        MsgBox "Data deleted.", vbInformation
    Else
        MsgBox "Nothing deleted.", vbExclamation
    End If

End Sub

CodePudding user response:

Please, try the next way. It does not need the backwards iteration, like it should be done in such a case. It uses an array, a dictionary and should be very fast. The problem of backwards iteration, if you delete each row at a time, is consuming Excel resources and make the code slower. The next code creates a dictionary Union range and delete it at the end:

Sub Delete_Rows_()
  Dim sh As Worksheet, lastRow As Long, arr, i As Long, dict As Object
  
  Set sh = ActiveSheet 'Worksheets("Email_Status_Bonus")
  lastRow = sh.cells(sh.rows.count, 7).End(xlUp).row
  arr = sh.Range("G1:G" & lastRow).value 'place the range in an array for faster iteration
  Set dict = CreateObject("Scripting.Dictionary")
  
  For i = 8 To UBound(arr)          'iterate beteen the array elements:
    If arr(i, 1) = "No" Then        'if condition is met:
        If Not dict.Exists(1) Then  'if dictionary with 1 key does  not exist:
            dict.Add 1, sh.Range("A" & i) 'create it, with a range as its item
        Else
            Set dict(1) = Union(dict(1), sh.Range("A" & i)) 'create a Union between existing and the new range
        End If
    End If
  Next i
  If Not dict(1) Is Nothing Then dict(1).EntireRow.Delete
End Sub

Using a dictionary in such a purpose is a fancy approach... It is the first time I tried it, to see how it works, like an experiment. A Union range can be set directly, too.

The dictionary approach may be used when you need more than one such a Union range, not knowing how many such ranges you need. The dictionary item may contain an array of Union ranges, adding such ranges when needed...

CodePudding user response:

We need to iterate from the last item, which in this case is a row, to the first when deleting. Deleting a row from a range shift the next rows changing there indeices.

Sub Delete_Rows()
    Dim StartRow As Integer
    Dim LastRow As Integer
    Dim ColNum As Integer
    Dim CellVal As String
    
    ColNum = 7
    StartRow = 8
    
    Worksheets("Email_Status_Bonus").Activate
    LastRow = Cells(Rows.Count, ColNum).End(xlUp).Row
    For i = LastRow To StartRow Step -1
        CellVal = Cells(i, ColNum).Value
        If CellVal = "No" Then
            Cells(i, ColNum).EntireRow.Delete
        End If
        StartRow = StartRow   1
    Next i
    
End Sub

Using Range.Autofilter on contiguous blocks of data will simplify and greatly speed up.

Sub DeleteVisisbleValues()
    
    Application.ScreenUpdating = False
    
    With ThisWorkbook.Worksheets("Email_Status_Bonus")
        Rem Target A1 and all the contiguous (connected) data cells
        With Range("A1").CurrentRegion
            Rem Clears Existing Filter
            .AutoFilter
            
            Rem Apply Filter
            .AutoFilter 7, "No"
            
            Rem Delete Visible Rows
            .Offset(1).Delete
        End With
    End With
    
    Application.ScreenUpdating = True

End Sub

CodePudding user response:

instead of looping backwards, you could insert "i = i - 1" after "Cells(i, ColNum).EntireRow.Delete"

CodePudding user response:

why You increase StartRow , in my opinion you must decrease 1 value of "i" when macro find "No"

Sub Delete_Rows()
    Dim StartRow As Integer
    Dim LastRow As Integer
    Dim ColNum As Integer
    Dim CellVal As String
    Dim i As Integer

    ColNum = 7
    StartRow = 8

    Worksheets("Sayfa2").Activate
    LastRow = Cells(Rows.Count, ColNum).End(xlUp).Row
    For i = StartRow To LastRow
        CellVal = Cells(i, ColNum).Value
        If CellVal = "No" Then
            Cells(i, ColNum).EntireRow.Delete
    i = i - 1
    End If
    Next i
End Sub
  • Related