87

I have an array of elements and need to remove certain ones from it. The problem is that JavaScript doesn't seem to have a for each loop and if I use a for loop I run into problems with it basically trying to check elements beyond the bounds of the array, or missing elements in the array because the indexes change. Let me show you what I mean:

var elements = [1, 5, 5, 3, 5, 2, 4];
for(var i = 0; i < elements.length; i++){
    if(elements[i] == 5){
        elements.splice(i, 1);
    }
}

The problem is that when elements[1] is removed, elements[2] becomes elements[1]. So first problem is that some elements are never examined. The other problem is that .length changes and if I hard code the bounds, then I might be trying to examine elements beyond the bounds of the array. So what's the best way to do this incredibly simple thing?

3

6 Answers 6

203

Start from the top!

var elements = [1, 5, 5, 3, 5, 2, 4];
for(var i = elements.length - 1; i >= 0; i--){
    if(elements[i] == 5){
        elements.splice(i, 1);
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

+1 for starting from the end to avoid skipping elements due to the changing length that the OP has mentioned.
for (var i = elements.length; i--;) is probably the more common way to write it.
Seriously? An O(n^2) answer, with 147 upvotes and no downvotes? Here's a downvote for you.
Isn't this just O(n)? It only loops through the entire array once.
@Tasik What is O() complexity of splice? It seems to be O(N), so if code would remove say 1/4 of array, in front - it would need to make (1/4*N) * (3/4*N) = 3/16N^2 operations, giving N^2 But see stackoverflow.com/a/54879636/4130619 so...
|
37

You could use the filter method here:

var elements = [1, 5, 5, 3, 5, 2, 4].filter(function(a){return a !== 5;});
//=> elements now [1,3,2,4]

Or if you don't want to touch elements:

var elementsfiltered
   ,elements = [1, 5, 5, 3, 5, 2, 4]
                .filter( function(a){if (a!==5) this.push(a); return true;},
                         elementsfiltered = [] );
   //=> elementsfiltered = [1,3,2,4], elements = [1, 5, 5, 3, 5, 2, 4]

See MDN documentation for filter

Alternatively you can extend the Array.prototype

Array.prototype.remove = Array.prototype.remove || function(val){
    var i = this.length;
    while(i--){
        if (this[i] === val){
            this.splice(i,1);
        }
    }
};
var elements = [1, 5, 5, 3, 5, 2, 4];
elements.remove(5);
//=> elements now [1,3,2,4]

3 Comments

While creating a new array, which filter does, is not a bad suggestion as a solution, the OP does actually ask about removing elements inline and it would seem best to give an example of that.
Seems like an unnecessary departure from the original code. OP could keep his existing code by decrementing i after the splice.
personally I feel filter is safer as decrementing i may lead to edge cases as if zero elements are there etc
10

var elements = [1, 5, 5, 3, 5, 2, 4];    
var i = elements.length;
while (i--) {
    if (elements[i] == 5) {
        elements.splice(i, 1);
    }
}
console.log(elements);

Comments

3

You could simply decrement i whenever you remove an item.

var elements = [1, 5, 5, 3, 5, 2, 4];

var l = elements.length;
for(var i = 0; i < l; i++){
    if(elements[i] == 5){
        elements.splice(i, 1);
        i--;
    }
}

console.log(elements);

Comments

1

Using Array.shift():

var array = [1, 2, 3, 'a', 'b', 'c'];
while (array.length > 0) {
  console.log(array.shift());
}

Edit: Probably does not suit the specs. I misread the question (only remove certain elements) and was too eager instead to add a method that was not mentioned yet...

1 Comment

As you say in your edit, this doesn't actually answer the question, however it does answer the question I was trying to search for. Thank you.
0

This is an example of using Array.indexOf, while and Array.splice to remove elements inline.

var elements = [1, 5, 5, 3, 5, 2, 4];
var remove = 5;
var index = elements.indexOf(remove);

while (index !== -1) {
    elements.splice(index, 1);
    index = elements.indexOf(remove);
}

console.log(elements);

On jsfiddle

15 Comments

downvoted for inefficient O(n^2) algorithm
I never made any claims on efficiency, and the question didn't concern that, but how you can deal with a changing array length when performing inline element removal. Here is a jsPerf so that you can give your efficient version as an answer and compare it with other answers posted. jsperf.com/soq-iterate-over-an-array-and-remove
the canonical method of dealing with a changing array length is to start from the end of the array and work backwards. Alternatively it's possible to work forwards from the current position and not increment if you've removed the current element. Instead, your method starts over from the zeroth element every time a match is found, thereby repeatedly going over and over the same elements. It's a very poor algorithm and shouldn't ever be used.
OK, that's weird - I tried that and (on this small sample set) it was substantially slower. I don't know why yet. jsperf.com/soq-iterate-over-an-array-and-remove/2
I changed the test case slightly - now the fromIndex case runs very slightly faster... jsperf.com/soq-iterate-over-an-array-and-remove/3
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.