185
var listToDelete = ['abc', 'efg'];

var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

How do I remove an object from the array by matching object property?

Only native JavaScript please.

I am having trouble using splice because length diminishes with each deletion. Using clone and splicing on orignal index still leaves you with the problem of diminishing length.

Dan Kanze
  • 18,485
  • 28
  • 81
  • 134
  • 2
    It's such a shame that there isn't a standard proper way to remove items from an object array. No wonder there are so many 3rd party scripts. It's a basic thing. – guitarlass Mar 01 '19 at 04:20
  • Possible duplicate of [Remove array element based on object property](https://stackoverflow.com/questions/15287865/remove-array-element-based-on-object-property) – vahdet Nov 01 '19 at 14:03

15 Answers15

183

I assume you used splice something like this?

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

All you need to do to fix the bug is decrement i for the next time around, then (and looping backwards is also an option):

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
        i--;
    }
}

To avoid linear-time deletions, you can write array elements you want to keep over the array:

var end = 0;

for (var i = 0; i < arrayOfObjects.length; i++) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) === -1) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

and to avoid linear-time lookups in a modern runtime, you can use a hash set:

const setToDelete = new Set(listToDelete);
let end = 0;

for (let i = 0; i < arrayOfObjects.length; i++) {
    const obj = arrayOfObjects[i];

    if (setToDelete.has(obj.id)) {
        arrayOfObjects[end++] = obj;
    }
}

arrayOfObjects.length = end;

which can be wrapped up in a nice function:

const filterInPlace = (array, predicate) => {
    let end = 0;

    for (let i = 0; i < array.length; i++) {
        const obj = array[i];

        if (predicate(obj)) {
            array[end++] = obj;
        }
    }

    array.length = end;
};

const toDelete = new Set(['abc', 'efg']);

const arrayOfObjects = [{id: 'abc', name: 'oh'},
                        {id: 'efg', name: 'em'},
                        {id: 'hij', name: 'ge'}];

filterInPlace(arrayOfObjects, obj => !toDelete.has(obj.id));
console.log(arrayOfObjects);

If you don’t need to do it in place, that’s Array#filter:

const toDelete = new Set(['abc', 'efg']);
const newArray = arrayOfObjects.filter(obj => !toDelete.has(obj.id));
Ry-
  • 218,210
  • 55
  • 464
  • 476
132

You can remove an item by one of its properties without using any 3rd party libs like this:

var removeIndex = array.map(item => item.id).indexOf("abc");

~removeIndex && array.splice(removeIndex, 1);
biberman
  • 5,606
  • 4
  • 11
  • 35
parliament
  • 21,544
  • 38
  • 148
  • 238
  • 28
    Or if you get confused by the tilde on last line, you might find this more clear: (removeIndex >= 0) && array.splice(removeIndex, 1); – wojjas Oct 11 '16 at 10:42
  • 1
    This answer is faster and more elegant solution than the selected one: https://jsben.ch/ld9op – Zack Lee May 03 '20 at 07:06
  • @ZackLee: For 5/10 elements it could be faster, sure. Here’s 50/100: https://jsben.ch/55g97 – Ry- Jun 21 '20 at 05:00
  • (plus you can skip the set construction for few elements, and then [it’s faster again](https://jsben.ch/0u2kf), even when you cache the array of ids. Also, the two approaches don’t treat duplicates the same way. Not sure if that’s important.) – Ry- Jun 21 '20 at 05:10
81

With lodash/underscore:

If you want to modify the existing array itself, then we have to use splice. Here is the little better/readable way using findWhere of underscore/lodash:

var items= [{id:'abc',name:'oh'}, // delete me
                  {id:'efg',name:'em'},
                  {id:'hij',name:'ge'}];

items.splice(_.indexOf(items, _.findWhere(items, { id : "abc"})), 1);

With ES5 or higher

(without lodash/underscore)

With ES5 onwards we have findIndex method on array, so its easier without lodash/underscore

items.splice(items.findIndex(function(i){
    return i.id === "abc";
}), 1);

(ES5 is supported in almost all morden browsers)

About findIndex, and its Browser compatibility

Rahul R.
  • 5,965
  • 3
  • 27
  • 38
  • how do I remove all items in array of object but i need the callback as well – Muhaimin Jun 09 '14 at 07:49
  • what do you mean by callback as well, you can use the above code to remove the object from array.. – Rahul R. Jun 09 '14 at 10:09
  • 1
    Nice one. I'll be adding this to my toolbelt – abyrne85 Jul 17 '15 at 10:02
  • another thing i want, There will be separate buttons for every object in array. if i want to delete that particular object in the array button clicked and that should be move to another secondary array. how to do it . i have used angular js ng-repeat to generate items. can you help me – Thilak Raj Jan 21 '16 at 04:51
  • Its same thing, on click get the id (or key) of the item, find it using finWhere, remove it using splice, push it to new Array. var toBeRemoved = _.findWhere(items, { id : "abc"}); items.splice(_.indexOf(toBeRemoved), 1); newArray.push(toBeRemoved); – Rahul R. Jan 21 '16 at 08:24
  • 10
    Love this ES5 suggestion, as it can even be written in a shorter way ```items.splice(items.findIndex(i => i.id === "abc"), 1)``` – bramchi May 15 '20 at 06:51
  • Thanks! @bramchi that ES5 did the trick for me in React Native. – Aqeeb Imtiaz Harun Feb 22 '21 at 21:19
  • 4
    Please be careful. This will not remove all the objects with the same 'id' in the array. – Jaseem Abbas May 05 '21 at 13:51
  • yes, as its based on id, it assumes it should be unique, it will remove just 1 element at a time. – Rahul R. May 06 '21 at 10:39
  • @RahulR. what about if you have an object like this with unique id's `[{id:1, text: 'user 1'}, {id:2, text: 'user 2'} {id: 3, text: 'user 3', children: [{id: 4, text: 'user 4'}, {id: 5, text: 'user 5'}]}]` ? – calin24 Dec 29 '21 at 20:45
  • @calin24, there could be multiple ways, but it will certainly require more code. One way, just flatten out your children array and put all users on parent array and use above functions. Or you could simply change findIndex anonymous function such that, it also look inside children property. – Rahul R. Dec 30 '21 at 08:52
39

To delete an object by it's id in given array;

const hero = [{'id' : 1, 'name' : 'hero1'}, {'id': 2, 'name' : 'hero2'}];
//remove hero1
const updatedHero = hero.filter(item => item.id !== 1);
Naresh Chennuri
  • 1,133
  • 11
  • 10
34

findIndex works for modern browsers:

var myArr = [{id:'a'},{id:'myid'},{id:'c'}];
var index = myArr.findIndex(function(o){
  return o.id === 'myid';
})
if (index !== -1) myArr.splice(index, 1);
Bouh
  • 1,303
  • 2
  • 10
  • 24
fatlinesofcode
  • 1,647
  • 18
  • 22
12

Check this out using Set and ES5 filter.

  let result = arrayOfObjects.filter( el => (-1 == listToDelete.indexOf(el.id)) );
  console.log(result);

Here is JsFiddle: https://jsfiddle.net/jsq0a0p1/1/

nCardot
  • 5,992
  • 6
  • 47
  • 83
Miroslav Savovski
  • 2,300
  • 2
  • 10
  • 12
9

If you just want to remove it from the existing array and not create a new one, try:

var items = [{Id: 1},{Id: 2},{Id: 3}];
items.splice(_.indexOf(items, _.find(items, function (item) { return item.Id === 2; })), 1);
user2704940
  • 115
  • 1
  • 2
7

Loop in reverse by decrementing i to avoid the problem:

for (var i = arrayOfObjects.length - 1; i >= 0; i--) {
    var obj = arrayOfObjects[i];

    if (listToDelete.indexOf(obj.id) !== -1) {
        arrayOfObjects.splice(i, 1);
    }
}

Or use filter:

var newArray = arrayOfObjects.filter(function(obj) {
    return listToDelete.indexOf(obj.id) === -1;
});
Felix Rabe
  • 4,206
  • 4
  • 25
  • 34
4

Only native JavaScript please.

As an alternative, more "functional" solution, working on ECMAScript 5, you could use:

var listToDelete = ['abc', 'efg'];
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}]; // all that should remain

arrayOfObjects.reduceRight(function(acc, obj, idx) {
    if (listToDelete.indexOf(obj.id) > -1)
        arrayOfObjects.splice(idx,1);
}, 0); // initial value set to avoid issues with the first item and
       // when the array is empty.

console.log(arrayOfObjects);
[ { id: 'hij', name: 'ge' } ]

According to the definition of 'Array.prototype.reduceRight' in ECMA-262:

reduceRight does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.

So this is a valid usage of reduceRight.

Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125
1
var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

as per your answer will be like this. when you click some particular object send the index in the param for the delete me function. This simple code will work like charm.

function deleteme(i){
    if (i > -1) {
      arrayOfObjects.splice(i, 1);
    }
}
Subhojit Mondal
  • 453
  • 7
  • 17
0

If you like short and self descriptive parameters or if you don't want to use splice and go with a straight forward filter or if you are simply a SQL person like me:

function removeFromArrayOfHash(p_array_of_hash, p_key, p_value_to_remove){
    return p_array_of_hash.filter((l_cur_row) => {return l_cur_row[p_key] != p_value_to_remove});
}

And a sample usage:

l_test_arr = 
[
    {
         post_id: 1,
        post_content: "Hey I am the first hash with id 1"
    },
    {
        post_id: 2,
        post_content: "This is item 2"
    },
    {
        post_id: 1,
        post_content: "And I am the second hash with id 1"
    },
    {
        post_id: 3,
        post_content: "This is item 3"
    },
 ];



 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 2); // gives both of the post_id 1 hashes and the post_id 3
 l_test_arr = removeFromArrayOfHash(l_test_arr, "post_id", 1); // gives only post_id 3 (since 1 was removed in previous line)
Mehmet Kaplan
  • 1,723
  • 2
  • 20
  • 43
0

with filter & indexOf

withLodash = _.filter(arrayOfObjects, (obj) => (listToDelete.indexOf(obj.id) === -1));
withoutLodash = arrayOfObjects.filter(obj => listToDelete.indexOf(obj.id) === -1);

with filter & includes

withLodash = _.filter(arrayOfObjects, (obj) => (!listToDelete.includes(obj.id)))
withoutLodash = arrayOfObjects.filter(obj => !listToDelete.includes(obj.id));
user3437231
  • 262
  • 4
  • 5
0

You can use filter. This method always returns the element if the condition is true. So if you want to remove by id you must keep all the element that doesn't match with the given id. Here is an example:

arrayOfObjects = arrayOfObjects.filter(obj => obj.id != idToRemove)

David Benitez Riba
  • 848
  • 1
  • 9
  • 11
0

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.


In your case

To specifically answer your question which is this:

var listToDelete = ['abc', 'efg'];

var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

Here is the answer:

listToDelete.forEach(x => arrayOfObjects.splice(arrayOfObjects.findIndex(n => n.id === x), 1));
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
0

var listToDelete = ['abc', 'efg'];

var arrayOfObjects = [{id:'abc',name:'oh'}, // delete me
                      {id:'efg',name:'em'}, // delete me
                      {id:'hij',name:'ge'}] // all that should remain

var result = arrayOfObjects.filter(object => !listToDelete.some(toDelete => toDelete === object.id));

console.log(result);
David Jesus
  • 1,981
  • 2
  • 29
  • 34