By dzm


2012-03-27 01:44:07 8 Comments

I have the following for loop, and when I use splice() to remove an item, I then get that 'seconds' is undefined. I could check if it's undefined, but I feel there's probably a more elegant way to do this. The desire is to simply delete an item and keep on going.

for (i = 0, len = Auction.auctions.length; i < len; i++) {
    auction = Auction.auctions[i];
    Auction.auctions[i]['seconds'] --;
    if (auction.seconds < 0) { 
        Auction.auctions.splice(i, 1);
    }           
}

11 comments

@0xc0de 2013-09-06 08:01:50

Although your question is about deleting elements from the array being iterated upon and not about removing elements (in addition to some other processing) efficiently, I think one should reconsider it if in similar situation.

The algorithmic complexity of this approach is O(n^2) as splice function and the for loop both iterate over the array (splice function shifts all elements of array in the worst case). Instead you can just push the required elements to the new array and then just assign that array to the desired variable (which was just iterated upon).

var newArray = [];
for (var i = 0, len = Auction.auctions.length; i < len; i++) {
    auction = Auction.auctions[i];
    auction.seconds--;
    if (!auction.seconds < 0) { 
        newArray.push(auction);
    }
}
Auction.auctions = newArray;

Since ES2015 we can use Array.prototype.filter to fit it all in one line:

Auction.auctions = Auction.auctions.filter(auction => --auction.seconds >= 0);

@Alexis Wilke 2014-05-24 04:55:08

I thinks your method is the safest, as it creates a new array we do not have to worry about the for() index to "break". With newer browsers, though we can use the Array.filter() function instead: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

@user1115652 2015-01-04 20:44:05

note that the filter version here doesn't decrement the seconds on every auction like the for loop version!

@qxz 2016-11-24 03:51:03

Does anyone have a benchmark on using filter vs. iterating backwards + splice?

@emanuel.virca 2017-04-06 14:38:01

Filter works for me.

@Lee 2017-12-01 11:52:50

Updated to use an arrow: auctions = auctions.filter(auction => --auction.seconds >=0) now whole thing fits on one line and the browser compatibility is good in 2017.

@0xc0de 2018-09-26 15:07:26

@dhilt Can you explain the reason of your edit to remove that code? I've not been able to keep up with ES recently, has anything changed?

@dhilt 2018-09-26 16:28:51

@0xc0de I walked through the answer revisions and compiled a new version of the update related to filter based on your pre-pre-revision that seems more suitable that the Lee's pre-revision. Sorry, if I'm making you uncomfortable.

@0xc0de 2018-09-26 16:46:51

@dhilt No worries, I was just wondering if something has changed since. Thanks for your efforts to improve/keep up-to-date this answer.

@user1106925 2012-03-27 01:50:52

The array is being re-indexed when you do a .splice(), which means you'll skip over an index when one is removed, and your cached .length is obsolete.

To fix it, you'd either need to decrement i after a .splice(), or simply iterate in reverse...

var i = Auction.auctions.length
while (i--) {
    ...
    if (...) { 
        Auction.auctions.splice(i, 1);
    } 
}

This way the re-indexing doesn't affect the next item in the iteration, since the indexing affects only the items from the current point to the end of the Array, and the next item in the iteration is lower than the current point.

@Dmitry Pashkevich 2012-10-25 13:08:51

Looks like the best solution to me. And the fastest one, by the way.

@0xc0de 2013-09-06 07:10:43

Nice! Why is this a community wiki btw?

@Darius 2013-09-30 13:04:55

Out of interest, this solution in its current guise presumably wont work if the array was an array of functions that you wanted to iterate over and invoke in the order that they were added to the array, for example?

@thedjaney 2014-09-16 02:48:40

i do mine by incrementing only if there is no delete

@ptf 2014-12-10 09:42:40

When you remove an entry from the array, then the next item you'll iterate over will be the same item as you are currently iteration over. Just something to keep in mind. And if you remove more than one item per iteration, you'll have to adjust len, else it will be out of bounds.

@super 2016-06-06 18:39:12

Pro tip: Never forget this solution

@frattaro 2017-04-02 12:55:44

Don't forget to break yourself out of the while loop when your iterator is less than zero.

@user1106925 2017-04-02 13:12:30

@frattaro: The condition will break the loop, at least in this case since the .length of an Array is never less than 0.

@frattaro 2017-04-02 13:27:45

@squint Oh, I see. the condition i-- will at some point return 0, and 0 will be evaluated to false. Neat.

@user1106925 2017-04-02 13:31:38

@frattaro: Yep, exactly. :)

@frattaro 2017-04-02 13:41:52

@squint I'd rather it was explicitly stated. Wouldn't use it in practice.

@user1106925 2017-04-02 13:45:57

@frattaro: Yeah it's one of those things that we see so often that we don't even think twice about it, but I agree that it isn't the clearest construct.

@Neon 2017-11-10 09:26:19

What was the reason for choosing a while loop over a for loop?

@Cruril 2017-11-30 22:18:36

@Neon Less code would be my guess. This solution is less than if you used a for loop for the same thing.

@3Dom 2018-02-14 03:47:30

var i = Auction.auctions.length - 1; (most folks will use this as the last(first) index to iterate over)

@Jake T. 2018-03-15 18:31:10

@3Dom the condition uses a post decrement, so say an array of length 5, it will look like while(5), which evaluates to true, 5 turns into 4, and 4 is used within the loop body as the index to get the next (first) item in the array, at the last index.

@Ryan Kopf 2018-05-14 13:52:06

I have never in my life seen someone iterate backwards over an array in Javascript like that, and it's the most brilliant thing I've ever seen. Why is this not more common.

@Rubinsh 2017-11-29 07:28:04

If you are e using ES6+ - why not just use Array.filter method?

Auction.auctions = Auction.auctions.filter((auction) => {
  auction['seconds'] --;
  return (auction.seconds > 0)
})  

Note that modifying the array element during filter iteration only works for objects and will not work for array of primitive values.

@user8533067 2017-08-29 14:06:09

You can just look through and use shift()

@Ivan 2017-08-29 14:41:00

Please add an example using this method.

@Dmitry Ragozin 2016-12-15 15:33:46

for (i = 0, len = Auction.auctions.length; i < len; i++) {
    auction = Auction.auctions[i];
    Auction.auctions[i]['seconds'] --;
    if (auction.seconds < 0) {
        Auction.auctions.splice(i, 1);
        i--;
        len--;
    }
}

@B001ᛦ 2016-12-15 15:53:44

A good answer will always have an explanation of what was done and why it was done in such a manner, not only for the OP but for future visitors to SO.

@Pablo 2016-11-17 16:40:28

Another simple solution to digest an array elements once:

while(Auction.auctions.length){
    // From first to last...
    var auction = Auction.auctions.shift();
    // From last to first...
    var auction = Auction.auctions.pop();

    // Do stuff with auction
}

@daniel.szaniszlo 2016-02-26 12:34:38

Here is another example for the proper use of splice. This example is about to remove 'attribute' from 'array'.

for (var i = array.length; i--;) {
    if (array[i] === 'attribute') {
        array.splice(i, 1);
    }
}

@Izzy 2017-11-13 10:49:43

shouldn't be that array.length-1 ?

@daniel.szaniszlo 2017-11-15 00:23:14

I think not because i-- reduces the index. If you do array.length-1 then the last element of array will be skipped

@Izzy 2017-11-15 11:53:50

Ah I see, but I must argue a bit that it is not so readable. Even tho I'm not beginner, at first glance I also didn't saw that inside check block of for loop you decrement i. for( var i = array.length-1; i>=0; i-- ) is much more cleaner (and it produces same effect)

@Zon 2016-01-28 15:54:43

Try to relay an array into newArray when looping:

var auctions = Auction.auctions;
var auctionIndex;
var auction;
var newAuctions = [];

for (
  auctionIndex = 0; 
  auctionIndex < Auction.auctions.length;
  auctionIndex++) {

  auction = auctions[auctionIndex];

  if (auction.seconds >= 0) { 
    newAuctions.push(
      auction);
  }    
}

Auction.auctions = newAuctions;

@frattaro 2015-01-24 03:49:17

This is a pretty common issue. The solution is to loop backwards:

for (var i = Auction.auctions.length - 1; i >= 0; i--) {
    Auction.auctions[i].seconds--;
    if (Auction.auctions[i].seconds < 0) { 
        Auction.auctions.splice(i, 1);
    }
}

It doesn't matter if you're popping them off of the end because the indices will be preserved as you go backwards.

@Aesthete 2013-09-06 08:12:44

Auction.auction = Auction.auctions.filter(function(el) {
  return --el["seconds"] > 0;
});

@Alexis Wilke 2014-05-24 04:17:57

This is a cool method, similar to 0xc0de above. Only problem, it was implemented in IE9+ only... so not backward compatible.

@Michael Scheper 2016-12-13 03:48:00

@AlexisWilke: Luckily, we're in the future now, and can dance on the graves of IE8 and its predecessors.

@Marc 2012-03-27 01:50:20

Recalculate the length each time through the loop instead of just at the outset, e.g.:

for (i = 0; i < Auction.auctions.length; i++) {
      auction = Auction.auctions[i];
      Auction.auctions[i]['seconds'] --;
      if (auction.seconds < 0) { 
          Auction.auctions.splice(i, 1);
          i--; //decrement
      }
}

That way you won't exceed the bounds.

EDIT: added a decrement in the if statement.

@RobG 2012-03-27 02:08:29

I thnk it's easier to just go backwards through the array, then you don't need to adjust the length at all.

@Doug S 2012-10-27 05:40:20

I also overlooked recalculating the array length after splicing in a loop. Funny how something so simple can have us scratching our heads for a moment.

Related Questions

Sponsored Content

69 Answered Questions

[SOLVED] How do I remove a particular element from an array in JavaScript?

  • 2011-04-23 22:17:18
  • Walker
  • 5130825 View
  • 6428 Score
  • 69 Answer
  • Tags:   javascript arrays

33 Answered Questions

[SOLVED] How do I loop through or enumerate a JavaScript object?

38 Answered Questions

[SOLVED] Loop through an array in JavaScript

31 Answered Questions

[SOLVED] How to break out of nested loops in Java?

  • 2009-05-20 09:07:43
  • boutta
  • 965473 View
  • 1579 Score
  • 31 Answer
  • Tags:   java loops

32 Answered Questions

[SOLVED] How to remove item from array by value?

  • 2010-10-17 17:43:34
  • MacMac
  • 730214 View
  • 675 Score
  • 32 Answer
  • Tags:   javascript arrays

19 Answered Questions

[SOLVED] How to loop through a plain JavaScript object with the objects as members?

  • 2009-05-28 16:18:14
  • edt
  • 1306439 View
  • 1302 Score
  • 19 Answer
  • Tags:   javascript

11 Answered Questions

[SOLVED] Looping through the content of a file in Bash

  • 2009-10-05 17:52:54
  • Peter Mortensen
  • 1126704 View
  • 992 Score
  • 11 Answer
  • Tags:   linux bash loops unix io

11 Answered Questions

[SOLVED] How to insert an item into an array at a specific index?

15 Answered Questions

[SOLVED] C# loop - break vs. continue

  • 2008-08-08 21:49:01
  • Seibar
  • 637282 View
  • 701 Score
  • 15 Answer
  • Tags:   c# loops enumeration

13 Answered Questions

[SOLVED] Is the recommendation to include CSS before JavaScript invalid?

Sponsored Content