user5113508
user5113508

Reputation:

How to compare two arrays and return the difference? (Working both ways?)

So I'm looking to achieve this in vanilla, ES6 JS. Please no jQuery! (Lodash is acceptable!)

Basically I have 2 arrays, 1 that contains objects, and 1 that contains ids.

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
];

const newIds = [1, 2, 3];

My current solution to this problem is as shown;

const difference = newIds.filter(n => !old.some(o => o.id === n));

This results in the return [3].

Basically I'm currently using this to determine if something is missing, and create it in my database if it is missing.

However, I need to then do the opposite. If something is removed, I need to compare and remove the item.

The problem with this is; This current solution only works "1 way" so I'm unable to do what I stated above.

Ideally I only want 1 statement whether it's creation or deletion, and I only want it to return the id's of stuff that is missing from either array.

Hopefully that makes sense.

Upvotes: 3

Views: 1322

Answers (7)

Phuti Semenya
Phuti Semenya

Reputation: 14

The following code will find the difference using lodash:

const difference = _.difference(newIds, _.map(old, 'id'));

Upvotes: 0

Fullstack Guy
Fullstack Guy

Reputation: 16908

We can do the same in vanilla es6 by using Array#filter and Array#includes.

Here, I have mapped the old array to only an array of ids by using Array#map and then using the filter and includes on both the arrays I have found out the symmetric difference between them.

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
];
const newIds = [2, 3];
const oldIds =  old.map(({id}) => id);

console.log(getDifference(oldIds, newIds));

function getDifference(oldIds, newIds){
  const diff = [...oldIds.filter(id => !newIds.includes(id)), ...newIds.filter(id => !oldIds.includes(id))];
  return diff;
}

We can do it another way using Array#reduce and Array#findIndex. Just reduce over both the arrays and return the elements which are not repeated more than once will give us the symmetric difference of the two arrays.

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
];
const newIds = [2, 3];
const oldIds =  old.map(({id}) => id);

console.log(getDifference(oldIds, newIds));

function getDifference(oldIds, newIds){
  return [...oldIds, ...newIds].reduce((acc, ele) => {
  if(!acc.includes(ele)){
     acc.push(ele);
  }else{
    acc.splice(acc.findIndex(e=> ele === e), 1);
  }
  return acc;
  },[]);
}

Upvotes: 1

Doug Coburn
Doug Coburn

Reputation: 2575

You could use _.get with a default value as an iteratee for _.differenceBy so that either the array of objects or the array of numbers could be on the left hand side.

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
];

const newIds = [1, 2, 3];

const diff = (a,b) => _.differenceBy(a, b, v => _.get(v, 'id', v))
console.log({ remove: diff(old, newIds), add: diff(newIds, old) })

const otherNewIds = [1];
console.log({ remove: diff(old, otherNewIds), add: diff(otherNewIds, old) })
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 0

Ishwar Patil
Ishwar Patil

Reputation: 1736

You can use xor function from lodash to get this. Before that, you can collect all the ids from the oldarray and use that ids array for xor.

Check this out.

const { xor } = _;
const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
  {
    id: 4,
    name: 'object_4',
  },
];

const newIds = [1, 2, 3];
const oldIds = old.map(o => o.id)
const diff = xor(newIds, oldIds);
console.log(diff)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Upvotes: 0

wang
wang

Reputation: 1780

Here is the solution using ES6:

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
  {
    id: 4,
    name: 'object_2',
  },
];

const newIds = [1, 2, 3];
const oldIds = old.map(o => o.id)

const difference = [...newIds.filter(id => oldIds.indexOf(id) === -1), ...oldIds.filter(id => newIds.indexOf(id) === -1)]

console.log(difference)

Upvotes: 0

Ray Toal
Ray Toal

Reputation: 88378

To find the values that appear in one but not the other use lodash's

_.xor(array1, array2);

In your case:

> const _ = require('lodash');

> const old = [
...   { id: 1, name: 'object_1' },
...   { id: 2, name: 'object_2' },
...   { id: 3, name: 'object_3' },
...   { id: 4, name: 'object_4' },
... ];

> const newIds = [1, 3, 5, 7];

> _.xor(old.map(x => x.id), newIds)
[ 2, 4, 5, 7 ]

Upvotes: 5

yajiv
yajiv

Reputation: 2941

Hope this helps. I know this not excatly what you want, but this can be one work around.

const old = [
  {
    id: 1,
    name: 'object_1',
  },
  {
    id: 2,
    name: 'object_2',
  },
  {
    id: 4,
    name: 'object_2',
  },
];

const newIds = [1, 2, 3];

var x1 = old.filter(x => !newIds.includes(x.id)).map(z => z.id)
             .concat(newIds.filter(x => !old.some(y => y.id === x)));
console.log(x1);

Upvotes: 0

Related Questions