Deleting rows (working backwards), but use a range variable?

前端 未结 3 1409
情歌与酒
情歌与酒 2021-01-03 09:17

Often times it\'s required that you go through a range of cells, and based on some criteria, delete an entire row.

In practice, it\'s best to start at the end

相关标签:
3条回答
  • 2021-01-03 09:39

    Yes, you can do it without a For i = statement. Just create a special range that you will delete once you finish your loop.

    Dim cel As Range, rng As Range
    Dim delRng As Range
    
    For Each cel In rng
        If cel.Value = "del" Then
            If delRng Is Nothing Then
                Set delRng = cel
            Else
                Set delRng = Union(delRng, cel)
            End If
        End If
    Next cel
    
    If Not delRng Is Nothing Then delRng.EntireRow.Delete
    

    And you don't even have to step backwards.

    0 讨论(0)
  • 2021-01-03 09:42

    The other way round

    "Is there a way to work backwards, with a range object, that doesn't require the use of a For i type loop?"

    In addition to @K-Davis ' valid solution, I demonstrate how to use the advanced filtering possibilties of the Application.Index method. It only expects three arguments:

    • a 2-dim datafield array v based on the entire data set
    • a 1-dim array of the row numbers to be maintained, called via a helper function getAr(v, 1), where argument 1 defines the 1st column
    • (a 1-dim array of all columns, created automatically via Evaluate)

    Instead of deleting rows, this approach used the whole data set (e.g. A2:C10 omitting an assumed title row) and writes back the filtered datafield array assigning it to rng again.

    Main procedure

    Sub DelRows()
      Dim rng As Range,  v
      Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A2:C10") ' << change to entire data range
    ' [1a] create 2-dim data field array (1-based)
      v = rng.Value2
    ' [1b] filter out rows to be deleted
      v = Application.Transpose(Application.Index(v, getAr(v, 1), Evaluate("row(1:" & rng.Columns.Count & ")")))
    ' [2] write data field back to resized range
      rng = ""                                  ' clear lines
      rng.Resize(UBound(v), UBound(v, 2)) = v
    End Sub
    

    Helper function getAr()

    Function getAr(v, ByVal colNo&) As Variant()
    ' Purpose: collect row numbers not to be deleted (criteria no "DEL" in 1st column)
    ' Note:    called by above procedure DelRows
      Dim ar, i&, n&
      ReDim ar(0 To UBound(v) - 1)
      For i = 1 To UBound(v)
          If UCase$(v(i, colNo)) <> "DEL" Then
             ar(n) = i: n = n + 1
          End If
      Next i
      ReDim Preserve ar(0 To n - 1): getAr = ar
    End Function
    

    Related SO link

    Cf. Insert new first column in datafield array without loops or API calls

    0 讨论(0)
  • 2021-01-03 09:55

    I know you said you don't like For i, but IMHO this is the cleanest way to go

    For i = rng.Rows.Count To 1 Step -1
        With rng.Cells(i, 2)
            If .Value = "del" then
                .Entirerow.Delete
            End If
        End With
    Next
    

    Note that the rng.Cells construct is relative to rng

    Eg if rng is A100:A1000 then rng.Cells(rng.Rows.Count, 1) refers to A1000

    0 讨论(0)
提交回复
热议问题