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
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.
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:
v
based on the entire data setgetAr(v, 1)
, where argument 1
defines the 1st columnEvaluate
)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
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