Dan Kanze
Dan Kanze

Reputation: 18595

remove objects from array by object property

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.

Upvotes: 192

Views: 409898

Answers (15)

David Jesus
David Jesus

Reputation: 2111

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

Upvotes: 0

CodingYoshi
CodingYoshi

Reputation: 26989

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

Upvotes: 0

Miroslav Savovski
Miroslav Savovski

Reputation: 2500

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/

Upvotes: 12

parliament
parliament

Reputation: 22904

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

Upvotes: 139

Rahul R.
Rahul R.

Reputation: 6461

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

Upvotes: 84

fatlinesofcode
fatlinesofcode

Reputation: 1667

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

Upvotes: 35

David Benitez Riba
David Benitez Riba

Reputation: 918

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)

Upvotes: 1

Naresh Chennuri
Naresh Chennuri

Reputation: 1153

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

Upvotes: 40

user3437231
user3437231

Reputation: 292

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

Upvotes: 0

Ry-
Ry-

Reputation: 224845

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

Upvotes: 191

Subhojit Mondal
Subhojit Mondal

Reputation: 473

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

Upvotes: 1

Mehmet Kaplan
Mehmet Kaplan

Reputation: 2342

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)

Upvotes: 0

Sylvain Leroux
Sylvain Leroux

Reputation: 51980

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.

Upvotes: 4

Felix Rabe
Felix Rabe

Reputation: 4286

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

Upvotes: 7

user2704940
user2704940

Reputation: 115

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

Upvotes: 9

Related Questions