Looping through array and removing items, without breaking for loop

JavascriptLoops

Javascript Problem Overview


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

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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.

Solution 3 - Javascript

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.

Solution 4 - Javascript

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

Solution 5 - Javascript

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

Solution 6 - Javascript

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.

Solution 7 - Javascript

Here is a simple linear time solution to this simple linear time problem.

When I run this snippet, with n = 1 million, each call to filterInPlace() takes .013 to .016 seconds. A quadratic solution (e.g. the accepted answer) would take a million times that, or so.

// Remove from array every item such that !condition(item).
function filterInPlace(array, condition) {
   var iOut = 0;
   for (var i = 0; i < array.length; i++)
     if (condition(array[i]))
       array[iOut++] = array[i];
   array.length = iOut;
}

// Try it out.  A quadratic solution would take a very long time.
var n = 1*1000*1000;
console.log("constructing array...");
var Auction = {auctions: []};
for (var i = 0; i < n; ++i) {
  Auction.auctions.push({seconds:1});
  Auction.auctions.push({seconds:2});
  Auction.auctions.push({seconds:0});
}
console.log("array length should be "+(3*n)+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be "+(2*n)+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be "+n+": ", Auction.auctions.length)
filterInPlace(Auction.auctions, function(auction) {return --auction.seconds >= 0; })
console.log("array length should be 0: ", Auction.auctions.length)

Note that this modifies the original array in place rather than creating a new array; doing it in place like this can be advantageous, e.g. in the case that the array is the program's single memory bottleneck; in that case, you don't want to create another array of the same size, even temporarily.

Solution 8 - Javascript

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
}

Solution 9 - Javascript

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

Solution 10 - Javascript

The normal for loop is more familiar for me, I just need to decrement the index each time I remove an item from the array

//5 trues , 5 falses
var arr1 = [false, false, true, true, false, true, false, true, true, false];

//remove falses from array
for (var i = 0; i < arr1.length; i++){
	if (arr1[i] === false){
		arr1.splice(i, 1);
		i--;// decrement index if item is removed
	}
}
console.log(arr1);// should be 5 trues

Solution 11 - Javascript

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;

Solution 12 - Javascript

Two examples that work:

(Example ONE)
// Remove from Listing the Items Checked in Checkbox for Delete
let temp_products_images = store.state.c_products.products_images
if (temp_products_images != null) {
    for (var l = temp_products_images.length; l--;) {
        // 'mark' is the checkbox field
        if (temp_products_images[l].mark == true) {
            store.state.c_products.products_images.splice(l,1);         // THIS WORKS
            // this.$delete(store.state.c_products.products_images,l);  // THIS ALSO WORKS
        }
    }
}

(Example TWO)
// Remove from Listing the Items Checked in Checkbox for Delete
let temp_products_images = store.state.c_products.products_images
if (temp_products_images != null) {
    let l = temp_products_images.length
    while (l--)
    {
        // 'mark' is the checkbox field
        if (temp_products_images[l].mark == true) {
            store.state.c_products.products_images.splice(l,1);         // THIS WORKS
            // this.$delete(store.state.c_products.products_images,l);  // THIS ALSO WORKS
        }
    }
}

Solution 13 - Javascript

Give this a try

RemoveItems.forEach((i, j) => {
    OriginalItems.splice((i - j), 1);
});

Solution 14 - Javascript

Deleting Parameters

        oldJson=[{firstName:'s1',lastName:'v1'},
                 {firstName:'s2',lastName:'v2'},
                 {firstName:'s3',lastName:'v3'}]
        
        newJson = oldJson.map(({...ele}) => {
          delete ele.firstName;
          return ele;
          })

it deletes and and create new array and as we are using spread operator on each objects so the original array objects are also remains unharmed

Solution 15 - Javascript

why waste cpu cycles on .splice? that operation has to go through the whole loop again and again to remove an element in an array.

why not just use traditional 2 flags in one loop?

const elements = [1, 5, 5, 3, 5, 2, 4];
const remove = 5

i = 0

for(let j = 0; j < elements.length; j++){
  if (elements[j] !== remove) {
    elements[i] = elements[j]
    i++
  }
}
elements.length = i

Solution 16 - Javascript

There are lot of wonderful answers on this thread already. However I wanted to share my experience when I tried to solve "remove nth element from array" in ES5 context.

JavaScript arrays have different methods to add/remove elements from start or end. These are:

arr.push(ele) - To add element(s) at the end of the array 
arr.unshift(ele) - To add element(s) at the beginning of the array
arr.pop() - To remove last element from the array 
arr.shift() - To remove first element from the array 

Essentially none of the above methods can be used directly to remove nth element from the array.

> A fact worth noting is that this is in contrast with java iterator's > using which it is possible to remove nth element for a collection > while iterating.

This basically leaves us with only one array method Array.splice to perform removal of nth element (there are other things you could do with these methods as well, but in the context of this question I am focusing on removal of elements):

Array.splice(index,1) - removes the element at the index 

Here is the code copied from original answer (with comments):

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter else it would run into IndexOutBounds exception
{
  if (arr[i] === "four" || arr[i] === "two") {
    //splice modifies the original array
    arr.splice(i, 1); //never runs into IndexOutBounds exception 
    console.log("Element removed. arr: ");

  } else {
    console.log("Element not removed. arr: ");
  }
  console.log(arr);
}

Another noteworthy method is Array.slice. However the return type of this method is the removed elements. Also this doesn't modify original array. Modified code snippet as follows:

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter 
{
  if (arr[i] === "four" || arr[i] === "two") {
    console.log("Element removed. arr: ");
    console.log(arr.slice(i, i + 1));
    console.log("Original array: ");
    console.log(arr);
  }
}

Having said that, we can still use Array.slice to remove nth element as shown below. However it is lot more code (hence inefficient)

var arr = ["one", "two", "three", "four"];
var i = arr.length; //initialize counter to array length 

while (i--) //decrement counter 
{
  if (arr[i] === "four" || arr[i] === "two") {
    console.log("Array after removal of ith element: ");
    arr = arr.slice(0, i).concat(arr.slice(i + 1));
    console.log(arr);
  }

}

> The Array.slice method is extremely important to achieve > immutability in functional programming à la redux

Solution 17 - Javascript

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

Solution 18 - Javascript

You can just look through and use shift()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondzmView Question on Stackoverflow
Solution 1 - Javascriptuser1106925View Answer on Stackoverflow
Solution 2 - JavascriptfrattaroView Answer on Stackoverflow
Solution 3 - JavascriptMarcView Answer on Stackoverflow
Solution 4 - Javascript0xc0deView Answer on Stackoverflow
Solution 5 - JavascriptAestheteView Answer on Stackoverflow
Solution 6 - JavascriptRubinshView Answer on Stackoverflow
Solution 7 - JavascriptDon HatchView Answer on Stackoverflow
Solution 8 - JavascriptPabloView Answer on Stackoverflow
Solution 9 - Javascriptdaniel.szaniszloView Answer on Stackoverflow
Solution 10 - JavascriptAccountant مView Answer on Stackoverflow
Solution 11 - JavascriptZonView Answer on Stackoverflow
Solution 12 - JavascriptFredView Answer on Stackoverflow
Solution 13 - JavascriptRickView Answer on Stackoverflow
Solution 14 - JavascriptAayush BhattacharyaView Answer on Stackoverflow
Solution 15 - JavascriptChris HuangView Answer on Stackoverflow
Solution 16 - Javascriptbp4DView Answer on Stackoverflow
Solution 17 - JavascriptDmitry RagozinView Answer on Stackoverflow
Solution 18 - Javascriptuser8533067View Answer on Stackoverflow