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












4















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









share|improve this question




















  • 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 | 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











  • Added an approach using the filtering possibilities of the Application.Index function.

    – T.M.
    Nov 2 '18 at 10:13
















4















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









share|improve this question




















  • 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 | 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











  • Added an approach using the filtering possibilities of the Application.Index function.

    – T.M.
    Nov 2 '18 at 10:13














4












4








4


3






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









share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 | 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











  • Added an approach using the filtering possibilities of the Application.Index function.

    – T.M.
    Nov 2 '18 at 10:13














  • 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 | 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











  • Added an approach using the filtering possibilities of the Application.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












3 Answers
3






active

oldest

votes


















2














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






share|improve this answer


























  • 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






  • 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






  • 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



















6














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.






share|improve this answer



















  • 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





















3














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






share|improve this answer





















  • 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 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











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
});


}
});














draft saved

draft discarded


















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









2














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






share|improve this answer


























  • 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






  • 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






  • 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
















2














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






share|improve this answer


























  • 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






  • 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






  • 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














2












2








2







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






share|improve this answer















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







share|improve this answer














share|improve this answer



share|improve this answer








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 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






  • 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






  • 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











  • @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





    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





    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













6














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.






share|improve this answer



















  • 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


















6














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.






share|improve this answer



















  • 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
















6












6








6







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.






share|improve this answer













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.







share|improve this answer












share|improve this answer



share|improve this answer










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
















  • 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













3














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






share|improve this answer





















  • 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 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
















3














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






share|improve this answer





















  • 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 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














3












3








3







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






share|improve this answer















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







share|improve this answer














share|improve this answer



share|improve this answer








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 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














  • 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 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








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


















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Florida Star v. B. J. F.

Error while running script in elastic search , gateway timeout

Adding quotations to stringified JSON object values