Deleting rows (working backwards), but use a range variable?
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 of the range, and work up.
Dim i as Long
For i = lastRow to 1 Step -1
If Cells(i, 2).Value = "del" then Rows(i).EntireRow.Delete
End if
However, most of the time I'm working with a Range
object.
Is there a way to work backwards, with a range object, that doesn't require the use of a For i
type loop?
Dim rng as Range, cel as Range
Set rng = Range("A1:A100")
For each cel in rng step -1
if cel.value = "del" then cel.EntireRow.Delete
next cel
This errors Expected: End of Statement
on the Step -1
portion, which I expected (no pun intended).
The idea is that I don't have to basically re-arrange my data into Cells()
when trying to work backwards on a Range
variable. I find it a little kludgy to use range variables a bunch, but when wanting to remove rows from that range, have to switch to using Cells([long],[long])
if that makes sense.
Edit: Just came up with this, but it still feels kludgy:
Dim k As Long, cel as Range
Set cel = rng.cells(rng.cells.count)
For k = cel.Row To rng.Cells(1).Row Step -1
If rng.Cells(k).Value = "del" Then rng.Cells(k).EntireRow.Delete
Next k
excel vba delete-row
add a comment |
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 of the range, and work up.
Dim i as Long
For i = lastRow to 1 Step -1
If Cells(i, 2).Value = "del" then Rows(i).EntireRow.Delete
End if
However, most of the time I'm working with a Range
object.
Is there a way to work backwards, with a range object, that doesn't require the use of a For i
type loop?
Dim rng as Range, cel as Range
Set rng = Range("A1:A100")
For each cel in rng step -1
if cel.value = "del" then cel.EntireRow.Delete
next cel
This errors Expected: End of Statement
on the Step -1
portion, which I expected (no pun intended).
The idea is that I don't have to basically re-arrange my data into Cells()
when trying to work backwards on a Range
variable. I find it a little kludgy to use range variables a bunch, but when wanting to remove rows from that range, have to switch to using Cells([long],[long])
if that makes sense.
Edit: Just came up with this, but it still feels kludgy:
Dim k As Long, cel as Range
Set cel = rng.cells(rng.cells.count)
For k = cel.Row To rng.Cells(1).Row Step -1
If rng.Cells(k).Value = "del" Then rng.Cells(k).EntireRow.Delete
Next k
excel vba delete-row
2
For Each
's order is implementation specific - it just calls_NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.
– Comintern
Nov 1 '18 at 18:42
1
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| ifrng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
Added an approach using the filtering possibilities of theApplication.Index
function.
– T.M.
Nov 2 '18 at 10:13
add a comment |
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 of the range, and work up.
Dim i as Long
For i = lastRow to 1 Step -1
If Cells(i, 2).Value = "del" then Rows(i).EntireRow.Delete
End if
However, most of the time I'm working with a Range
object.
Is there a way to work backwards, with a range object, that doesn't require the use of a For i
type loop?
Dim rng as Range, cel as Range
Set rng = Range("A1:A100")
For each cel in rng step -1
if cel.value = "del" then cel.EntireRow.Delete
next cel
This errors Expected: End of Statement
on the Step -1
portion, which I expected (no pun intended).
The idea is that I don't have to basically re-arrange my data into Cells()
when trying to work backwards on a Range
variable. I find it a little kludgy to use range variables a bunch, but when wanting to remove rows from that range, have to switch to using Cells([long],[long])
if that makes sense.
Edit: Just came up with this, but it still feels kludgy:
Dim k As Long, cel as Range
Set cel = rng.cells(rng.cells.count)
For k = cel.Row To rng.Cells(1).Row Step -1
If rng.Cells(k).Value = "del" Then rng.Cells(k).EntireRow.Delete
Next k
excel vba delete-row
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 of the range, and work up.
Dim i as Long
For i = lastRow to 1 Step -1
If Cells(i, 2).Value = "del" then Rows(i).EntireRow.Delete
End if
However, most of the time I'm working with a Range
object.
Is there a way to work backwards, with a range object, that doesn't require the use of a For i
type loop?
Dim rng as Range, cel as Range
Set rng = Range("A1:A100")
For each cel in rng step -1
if cel.value = "del" then cel.EntireRow.Delete
next cel
This errors Expected: End of Statement
on the Step -1
portion, which I expected (no pun intended).
The idea is that I don't have to basically re-arrange my data into Cells()
when trying to work backwards on a Range
variable. I find it a little kludgy to use range variables a bunch, but when wanting to remove rows from that range, have to switch to using Cells([long],[long])
if that makes sense.
Edit: Just came up with this, but it still feels kludgy:
Dim k As Long, cel as Range
Set cel = rng.cells(rng.cells.count)
For k = cel.Row To rng.Cells(1).Row Step -1
If rng.Cells(k).Value = "del" Then rng.Cells(k).EntireRow.Delete
Next k
excel vba delete-row
excel vba delete-row
edited Nov 1 '18 at 19:11
BruceWayne
asked Nov 1 '18 at 18:35
BruceWayneBruceWayne
17.1k93057
17.1k93057
2
For Each
's order is implementation specific - it just calls_NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.
– Comintern
Nov 1 '18 at 18:42
1
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| ifrng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
Added an approach using the filtering possibilities of theApplication.Index
function.
– T.M.
Nov 2 '18 at 10:13
add a comment |
2
For Each
's order is implementation specific - it just calls_NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.
– Comintern
Nov 1 '18 at 18:42
1
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| ifrng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
Added an approach using the filtering possibilities of theApplication.Index
function.
– T.M.
Nov 2 '18 at 10:13
2
2
For Each
's order is implementation specific - it just calls _NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.– Comintern
Nov 1 '18 at 18:42
For Each
's order is implementation specific - it just calls _NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.– Comintern
Nov 1 '18 at 18:42
1
1
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| if rng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| if rng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
Added an approach using the filtering possibilities of the
Application.Index
function.– T.M.
Nov 2 '18 at 10:13
Added an approach using the filtering possibilities of the
Application.Index
function.– T.M.
Nov 2 '18 at 10:13
add a comment |
3 Answers
3
active
oldest
votes
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
This is pretty good, except ifrng
isA100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.
– BruceWayne
Nov 1 '18 at 20:23
@bruce No, therng.Cells
construct is relative torng
. It'll start at the last row inrng
– chris neilsen
Nov 1 '18 at 20:57
1
ChangeCells(I, 1)
torng.Cells(i, 1)
. Cells on it's own is relative to the active sheet
– chris neilsen
Nov 2 '18 at 19:21
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
|
show 1 more comment
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.
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
add a comment |
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 argument1
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
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstratedIndex
method seems to be restricted to 65,536 rows that can be restructured at once.
– T.M.
Nov 3 '18 at 18:51
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53107362%2fdeleting-rows-working-backwards-but-use-a-range-variable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
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
This is pretty good, except ifrng
isA100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.
– BruceWayne
Nov 1 '18 at 20:23
@bruce No, therng.Cells
construct is relative torng
. It'll start at the last row inrng
– chris neilsen
Nov 1 '18 at 20:57
1
ChangeCells(I, 1)
torng.Cells(i, 1)
. Cells on it's own is relative to the active sheet
– chris neilsen
Nov 2 '18 at 19:21
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
|
show 1 more comment
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
This is pretty good, except ifrng
isA100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.
– BruceWayne
Nov 1 '18 at 20:23
@bruce No, therng.Cells
construct is relative torng
. It'll start at the last row inrng
– chris neilsen
Nov 1 '18 at 20:57
1
ChangeCells(I, 1)
torng.Cells(i, 1)
. Cells on it's own is relative to the active sheet
– chris neilsen
Nov 2 '18 at 19:21
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
|
show 1 more comment
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
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
edited Nov 1 '18 at 21:01
answered Nov 1 '18 at 19:50
chris neilsenchris neilsen
39.5k85693
39.5k85693
This is pretty good, except ifrng
isA100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.
– BruceWayne
Nov 1 '18 at 20:23
@bruce No, therng.Cells
construct is relative torng
. It'll start at the last row inrng
– chris neilsen
Nov 1 '18 at 20:57
1
ChangeCells(I, 1)
torng.Cells(i, 1)
. Cells on it's own is relative to the active sheet
– chris neilsen
Nov 2 '18 at 19:21
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
|
show 1 more comment
This is pretty good, except ifrng
isA100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.
– BruceWayne
Nov 1 '18 at 20:23
@bruce No, therng.Cells
construct is relative torng
. It'll start at the last row inrng
– chris neilsen
Nov 1 '18 at 20:57
1
ChangeCells(I, 1)
torng.Cells(i, 1)
. Cells on it's own is relative to the active sheet
– chris neilsen
Nov 2 '18 at 19:21
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
This is pretty good, except if
rng
is A100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.– BruceWayne
Nov 1 '18 at 20:23
This is pretty good, except if
rng
is A100:A1000
, it'll start at row 900, no? I think @ScottCraner's comment would be the way to use this type of loop.– BruceWayne
Nov 1 '18 at 20:23
@bruce No, the
rng.Cells
construct is relative to rng
. It'll start at the last row in rng
– chris neilsen
Nov 1 '18 at 20:57
@bruce No, the
rng.Cells
construct is relative to rng
. It'll start at the last row in rng
– chris neilsen
Nov 1 '18 at 20:57
1
1
Change
Cells(I, 1)
to rng.Cells(i, 1)
. Cells on it's own is relative to the active sheet– chris neilsen
Nov 2 '18 at 19:21
Change
Cells(I, 1)
to rng.Cells(i, 1)
. Cells on it's own is relative to the active sheet– chris neilsen
Nov 2 '18 at 19:21
1
1
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
Aha! Thanks for clarifying, I didn't catch that.
– BruceWayne
Nov 2 '18 at 19:27
1
1
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
@Bruce your Q as asked is about iterating a range backwards. This answer addresses that directly, and makes no comments on if it's the "best" method to delete rows from a range. If your real interest is in other methods to delete some rows from a range, that's been asked many times before, and there are lots of good answers demonstrating quite a few different methods. Which one is best for you will depend on your situation - how big is your range, what proportion is to be deleted, are there formulas in the range or refer to the range, can you sort, etc
– chris neilsen
Nov 2 '18 at 19:33
|
show 1 more comment
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.
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
add a comment |
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.
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
add a comment |
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.
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.
answered Nov 1 '18 at 21:08
K.DᴀᴠɪsK.Dᴀᴠɪs
7,071112339
7,071112339
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
add a comment |
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
1
1
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
Nice & straight forward +1 :-)
– T.M.
Nov 2 '18 at 10:40
1
1
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
Ooooh I like this a lot. While it does require the use of another variable, it lets me keep the method of using a range object without switching "styles". This is great! (And I honestly expected to need some other variable or helper, so that's totally fine!). Also, very concise and logical :D This is the best answer so far (I'll leave a day or so more just in case someone else pops in, before marking as The Answer)
– BruceWayne
Nov 2 '18 at 15:24
add a comment |
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 argument1
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
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstratedIndex
method seems to be restricted to 65,536 rows that can be restructured at once.
– T.M.
Nov 3 '18 at 18:51
add a comment |
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 argument1
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
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstratedIndex
method seems to be restricted to 65,536 rows that can be restructured at once.
– T.M.
Nov 3 '18 at 18:51
add a comment |
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 argument1
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
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 argument1
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
edited Nov 2 '18 at 9:53
answered Nov 2 '18 at 9:40
T.M.T.M.
2,0963725
2,0963725
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstratedIndex
method seems to be restricted to 65,536 rows that can be restructured at once.
– T.M.
Nov 3 '18 at 18:51
add a comment |
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstratedIndex
method seems to be restricted to 65,536 rows that can be restructured at once.
– T.M.
Nov 3 '18 at 18:51
1
1
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
Interesting approach
– K.Dᴀᴠɪs
Nov 2 '18 at 13:54
1
1
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Woah, thanks for this. Looks really interesting, and I'll be sure to study it. Cheers!
– BruceWayne
Nov 2 '18 at 15:25
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Additional hint: the applied array method is faster than looping through a range in VBA. Not representative, but for 32,000 rows with more than 20% "Del" rows it took me only 0.34 seconds on my computer, for 1,000 rows just 0.25 seconds. Test it by yourself :-)
– T.M.
Nov 3 '18 at 18:47
Further hint: the demonstrated
Index
method seems to be restricted to 65,536 rows that can be restructured at once.– T.M.
Nov 3 '18 at 18:51
Further hint: the demonstrated
Index
method seems to be restricted to 65,536 rows that can be restructured at once.– T.M.
Nov 3 '18 at 18:51
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53107362%2fdeleting-rows-working-backwards-but-use-a-range-variable%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
For Each
's order is implementation specific - it just calls_NewEnum
to get the next item in the iteration, and that means you have no control over the ordering. It's entirely up to the enumerable interface you're using.– Comintern
Nov 1 '18 at 18:42
1
For i = rng.row + rng.rows.count - 1 to rng.row Step -1
| ifrng.Cells(i-rng.row+1,1).Value = "del" then rows(i).Delete
– Scott Craner
Nov 1 '18 at 19:08
@ScottCraner - Yeah, I was thinking something like that. Still feels a little kludgy, as we can't use "clean" variables, but I get it...
– BruceWayne
Nov 1 '18 at 19:47
Added an approach using the filtering possibilities of the
Application.Index
function.– T.M.
Nov 2 '18 at 10:13