15

Assume we have the following arrays of objects to be compared based on property id:

a = [{'id':'1', 'name':'a1'}, {'id':'2', 'name':'a2'}, {'id':'3', 'name':'a3'}]

and

b = [[{'id':'2', 'name':'a2'}, ]

How can I subtract b from a? So that we have c = a - b which should be equal to [ {'id':'1', 'name':'a1'}, {'id':'3', 'name':'a3'}].

I have tried using this:

var c= a.filter(function(item) {
                    return !b.includes(item.id);
                });

but still not working.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Codec
  • 193
  • 1
  • 2
  • 7
  • 1
    Possible duplicate of [What is the fastest or most elegant way to compute a set difference using Javascript arrays?](https://stackoverflow.com/questions/1723168/what-is-the-fastest-or-most-elegant-way-to-compute-a-set-difference-using-javasc) – Jordumus Oct 30 '17 at 14:31
  • without a filter you can do it in a simple loop where you need to check arrays for the same values: `if(a[i].id == b[j].id){ a.splice(i, 1); i-=1; }` – Aleksey Solovey Oct 30 '17 at 14:34
  • @Jordumus, in my question I have objects which should be compared based on `id` attribute. Its not an array of integers or strings as the duplicate question. – Codec Oct 30 '17 at 14:35
  • @AlekseySolovey you woud have a loop nested in another loop in your proposed solution. – Codec Oct 30 '17 at 14:37
  • @Codec which is a valid JavaScript code – Aleksey Solovey Oct 30 '17 at 14:38

4 Answers4

28

How about this solution? It assumes that 'b' is also an array so for each element of 'a' you check if there is a matching object in 'b'. If there is a matching object then return a false in the filter function so that that element is discarded.

var a = [{
  'id': '1',
  'name': 'a1'
}, {
  'id': '2',
  'name': 'a2'
}, {
  'id': '3',
  'name': 'a3'
}]
var b = [{
  'id': '2',
  'name': 'a2'
}]

var c = a.filter(function(objFromA) {
  return !b.find(function(objFromB) {
    return objFromA.id === objFromB.id
  })
})

console.log(c)
John
  • 1,313
  • 9
  • 21
19

Here is a nice one line answer :)

Basically, you can filter, as you were trying to do already. Then you can also filter b for each a element and if the length of the filtered b is zero, then you return true because that means the a element is unique to a.

var a = [{
  'id': '1',
  'name': 'a1'
}, {
  'id': '2',
  'name': 'a2'
}, {
  'id': '3',
  'name': 'a3'
}];

var b = [{
  'id': '2',
  'name': 'a2'
}];

c = a.filter( x => !b.filter( y => y.id === x.id).length);
console.log(c);
crypto_rob
  • 191
  • 4
10

Easy with new ES6 Syntax

Second and Third way are more performant i guess....

a.filter(i => !b.filter(y => y.id === i.id).length); // One Way
a.filter(i => !b.find(f => f.id === i.id)); // Second Way
a.filter(i => b.findIndex(f => f.id === i.id)) // Third Way
Kabeer Jaffri
  • 652
  • 1
  • 9
  • 9
1

First, you build just a map of the ids you want to delete. Then, you filter your first array with it, like that:

var a = [{
  'id': '1',
  'name': 'a1'
}, {
  'id': '2',
  'name': 'a2'
}, {
  'id': '3',
  'name': 'a3'
}];
var b = [{
  'id': '2',
  'name': 'a2'
}];

var idsToDelete = b.map(function(elt) {return elt.id;});
var result = a.filter(function(elt) {return idsToDelete.indexOf(elt.id) === -1;});
console.log(result)
sjahan
  • 5,720
  • 3
  • 19
  • 42