154

I was wondering how I'd go about implementing a method in JavaScript that removes all elements of an array that clear a certain condition. (Preferably without using jQuery.)

ar = [ 1, 2, 3, 4 ];
ar.removeIf( function(item, idx) {
    return item > 3;
});

The above would go through each item in the array and remove all those that return true for the condition (in the example, item > 3).

I'm just starting out in JavaScript and was wondering if anyone knew of a short efficient way to get this done.

--Update1--

It would also be great if the condition could work on object properties as well:

ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return item.str == "c";
});

Where the item would be removed if item.str == "c"

--Update2--

It would be nice if index conditions could work as well:

ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
ar.removeIf( function(item, idx) {
    return idx == 2;
});
0

13 Answers 13

197

You can use Array filter method.

The code would look like this:

ar = [1, 2, 3, 4];
ar = ar.filter(item => !(item > 3));
console.log(ar) // [1, 2, 3]

Sign up to request clarification or add additional context in comments.

4 Comments

If you translate !(item > 3) into human language, it will be item <= 3.
Keep in mind that Array.filter method creates a new array. If you reference your array in another variable, the reference will be broken afterwards: ar1 = [1, 2, 3, 4]; ar2 = ar1; ar1 = ar1.filter(item => !(item > 3)); console.log(ar1); console.log(ar2);
@Miha_x64 In JavaScript, !(item > 3) isn't exactly the same as item <= 3. If that array contains undefined items, !(item > 3) will be true while item <= 3 will be false.
This doesn't remove items from the array. This produces a new array and assigns to the ar variable. If any other variables have referenced the initial array in ar, they are not updated/affected. I would not consider this answer
72

You could add your own method to Array that does something similar, if filter does not work for you.

Array.prototype.removeIf = function(callback) {
    var i = 0;
    while (i < this.length) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
        else {
            ++i;
        }
    }
};

To me, that's one of the coolest features of JavaScript. Ian pointed out a more efficient way to do the same thing. Considering that it's JavaScript, every bit helps:

Array.prototype.removeIf = function(callback) {
    var i = this.length;
    while (i--) {
        if (callback(this[i], i)) {
            this.splice(i, 1);
        }
    }
};

This avoids the need to even worry about the updating length or catching the next item, as you work your way left rather than right.

7 Comments

Thanks, works great. I like how this takes into consideration the index changes through splicing during iteration.
That's a little messy. Try jsfiddle.net/n8JEy/4 - and you didn't pass i to the callback as the second param
@Ian I tend to prefer readability, but your solution is certainly more efficient. I also added the index as the second parameter (simply ignored it originally as it was not used, and I assumed that he didn't need it).
@dk123 As a newcomer to JavaScript, you are free to control the parameters of such callbacks as you see fit, and you can add whatever you want to them so long as the information is available (e.g., you could add the current array's length using this.length as a third parameter, if you had some inclination).
Mutating prototype of a predefined class is risky. Should be done only in polyfill-ish fashion, i. e. to add members which are already declared in specs but not implemented by browsers yet. Thanks for the solution, I'm using it as a local function.
|
48

You can use Array.filter(), which does the opposite:

ar.filter(function(item, idx) {
    return item <= 3;
});

9 Comments

As a head's up that is not supported in IE8 or below. You should also either negate the original expression, or it should be <=.
Doesn't Array.filter() return a new array with the filtered elements? Is there something that just removes the elements from the array?
@dk123: There's nothing pre-made. You'll have to write a for loop.,
Making a new array with Array.filter will likely be faster than manually splicing out of the given array.
@Blender That's very interesting. I swear I just looked into something recently comparing the two and it was the opposite. Stupid comment of mine deleted. Thanks for providing that for proof :)
|
10

You can use lodash.remove

var array = [1, 2, 3, 4];
var evens = _.remove(array, function(n) {
  return n % 2 == 0;
});

console.log(array);
// => [1, 3]

console.log(evens);
// => [2, 4]

Comments

10

Make it a one-liner with arrow function:

ar = ar.filter(i => i > 3);

Comments

8

if you need to remove exactly one item, and you know for sure that the item exists, you can use splice:

ar.splice(ar.findIndex(el => el.id === ID_TO_REMOVE), 1);

http://jsfiddle.net/oriadam/72kgprw5/

Warning the above fails when item is not found, so use it when you already checked it is there for some reason.

EDIT: filter works, but in some cases you may prefer to use splice. it is ~10 times faster than filter and changes the array in situ.

For example, when you need to work with the item before removing it:

let foundIdx=arr.findIndex(x=>x.id=123);
if (foundIdx>=0) {
    const found=arr[foundIdx];
    /* ...do things with found item... */
    arr.splice(foundIdx,1); 
}

2 Comments

splice() + findIndex() isn't faster than just filter() 'ing out the removed values. Or is it in some cases?
@SaturnK yeah it's faster. created a simple benchmark removing 1000 items, filter took 7500ms and splice took 500ms, that's huge. here: jsfiddle.net/oriadam/tqu9c470/1
4

simply write the following example if condition could work on object properties as well

var ar = [ {num:1, str:"a"}, {num:2, str:"b"}, {num:3, str:"c"} ];
var newArray = [];
for (var i = 0, len = ar.length; i<len; i++) {
        if (ar[i].str == "b") 
        {newArray.push(ar[i]);};
 };
console.log(newArray);

See the example Live Example

Comments

3

I love these kinds of questions and just a different version from me too... :)

Array.prototype.removeIf = function(expression) {
   var res = [];
    for(var idx=0; idx<this.length; idx++)
    {
      var currentItem = this[idx];
        if(!expression(currentItem))
        {
            res.push(currentItem);
        }
    }
    return res;
}

ar = [ 1, 2, 3, 4 ];
var result = ar.removeIf(expCallBack);


console.log(result);
function expCallBack(item)
{
    return item > 3;
}

2 Comments

Could you perhaps modify(or add another method) your code a bit to actually remove elements instead of returning a new array? Thanks for the reply.
whoever downvoted this so wrong. this is the best perfoming answer out of all of them. check the test by @Blender jsperf.com/splice-vs-filter. it's at least 2x faster and as much as 15x faster in the latest versions of Firefox
3

My solution for an array of numbers would be:

ar = ar.filter(item => item < 4);

2 Comments

Best answer here, simple and elegant
@Marcel, it is a good answer if you don't mind losing the reference to the original array, as per this comment: stackoverflow.com/questions/15995963/… The question has no reassignment, implying a solution that manipulates the original array is what is requested.
2

Incorrect way

First of all, any answer that suggests to use filter does not actually remove the item. Here is a quick test:

var numbers = [1, 2, 2, 3];
numbers.filter(x => x === 2);
console.log(numbers.length);

In the above, the numbers array will stay intact (nothing will be removed). The filter method returns a new array with all the elements that satisfy the condition x === 2 but the original array is left intact.

Sure you can do this:

var numbers = [1, 2, 2, 3];
numbers = numbers.filter(x => x === 2);
console.log(numbers.length);

But that is simply assigning a new array to numbers.


Correct way to remove items from array

One of the correct ways, there are more than 1, is to do it as following. Please keep in mind, the example here intentionally has duplicated items so the removal of duplicates can be taken into consideration.

var numbers = [1, 2, 2, 3];

// Find all items you wish to remove
// If array has objects, then change condition to x.someProperty === someValue
var numbersToRemove = numbers.filter(x => x === 2);

// Now remove them
numbersToRemove.forEach(x => numbers.splice(numbers.findIndex(n => n === x), 1));

// Now check (this is obviously just to test)
console.log(numbers.length);
console.log(numbers);

Now you will notice length returns 2 indicating only numbers 1 and 3 are remaining in the array.

Comments

0

For the in-place remove, my solution is

ar.filter(item => !(item > 3))
  .forEach(obsoleteItem => ar.splice(ar.indexOf(obsoleteItem), 1));

Comments

-1

If you want to remove items from an array and have them also leave the original array (like you are picking out your favourite sweets) you could try this:

function takeFromList(array, condition) {

    const taken = []

    for (let i = array.length -1; i >= 0 ; i--) {
        const y = array[i]
        
        if(condition(y)){
            taken.unshift(...array.splice(i, 1))           
        }
        
    }
    return taken
}

const pickAndMix = ["Skittles", "Fizzers", "Jelly Babies" ,"Skittles", 
    "Haribo Goldbears", "Love Hearts", "Haribo Goldbears", "Skittles", "Fizzers"]

const selection = takeFromList(pickAndMix, (x) => x === "Skittles")

console.log(selection)
console.log(pickAndMix)

// returns ["Skittles", "Skittles", "Skittles"]
// returns ["Fizzers", "Jelly Babies", "Haribo Goldbears", "Love Hearts", 
//  "Haribo Goldbears", "Fizzers"]

Comments

-1

This is the new best solution making the previous solutions complex and slow,
using the power .findLast() widely available in JavaScript since 2022.

array.findLast((elem, idx, arr) => (test(elem) && arr.splice(idx, 1), false))

Wrapped into an Array extension:

Array.prototype.removeAllBy = function(predicate) {
   // removes all elements matching the given predicate
   // `findLast` enables efficient array iteration
   this.findLast((elem, idx) => (predicate(elem) && this.splice(idx, 1), false))
}

arr = [1,2,3,4,5,6]
arr.removeBy(e => e > 3)
console.log(arr) // [1, 2, 3]

This is a simple and fast solution while keeping readibility, mutating the original array in place.

Most of the other answers using arr.forEach then arr.findIndex are iterating two times over the array for finding the items to removed. Mine does it only once, that is around 33% faster on average for this part.

The accepted answer iterates only once but it uses manual iteration. That is hard to read and slightly slower than built-in methods profiting from natvie engine optimizations. It also introduces more array reindexing load when removing elements by splice() caused by the forward searching.

3 Comments

@Ry there is a misunderstanding here. oriadam’s answer removes exactly one matching element (if present), my removes all matching - this difference is fundamental, as the OP asked to remove all elements. Although the two approaches may look similar at the first glance, they solve different problems in different ways.
@Ry I am happy to clarify further if needed - just want to ensure the difference in behavior is recognized, since it directly affects how the OP’s problem is solved.
Oh, I missed the comma-false, thank you for pointing that out. That makes this a small optimization compared to iterating forwards, but still O(n^2), which is not ideal (and the difference doesn’t come from iterating once vs. twice). The ideal time is O(n), like with filter.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.