bklups
bklups

Reputation: 300

Compare 2 arrays in javascript and extract differences

Array 1 is the result of the data from a localstorage

Array 2 is, for the same IDs (329, 307, 355), the result after treatment

So i need to compare both to notify what changed

Array 1 :

[{"329":["45738","45737","45736"]},{"307":["45467","45468"]},{"355":["47921"]}]

Array 2 :

[{"355":["47921","45922"]},{"329":["45738","45737","45736"]},{"307":[]}]

I need to compare Array 2 with Array 1 and extract differences.

In this example i want to have for result

[{"355":["45922"]},{"307":[]}]

I try to adapt this code :

var compareJSON = function(obj1, obj2) {
  var ret = {};
  for(var i in obj2) {
    if(!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
      ret[i] = obj2[i];
    }
  }
  return ret;
};

Runnable:

var array1 = [{
    "329": ["45738", "45737", "45736"]
  }, {
    "307": ["45467", "45468"]
  }, {
    "355": ["47921"]
  }],
  array2 = [{
    "355": ["47921", "45922"]
  }, {
    "329": ["45738", "45737", "45736"]
  }, {
    "307": []
  }]

var compareJSON = function(obj1, obj2) {
  var ret = {};
  for (var i in obj2) {
    if (!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
      ret[i] = obj2[i];
    }
  }
  return ret;
};

console.log(compareJSON(array1, array2));

But, either I have nothing or I have the whole table

Upvotes: 0

Views: 159

Answers (3)

alexanderbird
alexanderbird

Reputation: 4198

I've used the deep-diff package in npm for this sort of thing before:

It may be more detail than you want though - here's an example from the readme of the output format:

[ { kind: 'E',
  path: [ 'name' ],
  lhs: 'my object',
  rhs: 'updated object' },
{ kind: 'E',
  path: [ 'details', 'with', 2 ],
    lhs: 'elements',
    rhs: 'more' },
{ kind: 'A',
  path: [ 'details', 'with' ],
  index: 3,
  item: { kind: 'N', rhs: 'elements' } },
{ kind: 'A',
  path: [ 'details', 'with' ],
  index: 4,
  item: { kind: 'N', rhs: { than: 'before' } } } ]

Checkout the readme on the github page linked above for details about what it all means, or try it out for yourself online using runkit

But in order for this to work you would have to do some sort of preprocessing:

Sort array based on first key of each element:

a1 = a1.sort((lhs, rhs) => {
  return parseInt(Object.keys(lhs)[0]) - parseInt(Object.keys(rhs)[0]);
})

If you sort both of the arrays by the first key of each element and then pass it to the diff tool, you get the following:

[
  {"kind":"A","path":[0,"307"],"index":0,"item":{"kind":"D","lhs":"45467"}},
  {"kind":"A","path":[0,"307"],"index":1,"item":{"kind":"D","lhs":"45468"}},
  {"kind":"A","path":[2,"355"],"index":1,"item":{"kind":"N","rhs":"45922"}}
]

If it were me I would probably merge all the array elements and diff the resulting object so you completely avoid any object order and duplicate key issues.

Alternative: merge array contents into one object

A naive merge might look like this:

a1Object = {}

a1.forEach((element) => { 
    Object.keys(element).forEach((key) => {
        a1Object[key] = element[key];
    });
})

Which produces the following diff:

[
  {"kind":"A","path":["307"],"index":0,"item":{"kind":"D","lhs":"45467"}},
  {"kind":"A","path":["307"],"index":1,"item":{"kind":"D","lhs":"45468"}},
  {"kind":"A","path":["355"],"index":1,"item":{"kind":"N","rhs":"45922"}}
]

Interpreting the diff output

  • there is a change in the Array value of 307 at index 0: 45467 has been Deleted
  • there is a change in the Array value of 307 at index 1: 45468 has been Deleted
  • there is a change in the Array value of 355 at index 1: 45467 has been Newly added

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386550

You could use a hash tbale and delete found items. If some items remains, then an empty array is taken to the result object.

var array1 = [{ 329: ["45738", "45737", "45736"] }, { 307: ["45467", "45468"] }, { 355: ["47921"] }],
    array2 = [{ 355: ["47921", "45922"] }, { 329: ["45738", "45737", "45736"] }, { 307: [] }],
    hash = {},
    result = [];

array1.forEach(function (o) {
    Object.keys(o).forEach(function (k) {
        hash[k] = hash[k] || {};
        o[k].forEach(function (a) {
            hash[k][a] = true;
        });
    });
});

array2.forEach(function (o) {
    var tempObject = {};
    Object.keys(o).forEach(function (k) {
        var tempArray = [];
        o[k].forEach(function (a) {
            if (hash[k][a]) {
                delete hash[k][a];
            } else {
                tempArray.push(a);
            }
        });
        if (tempArray.length || Object.keys(hash[k]).length) {
            tempObject[k] = tempArray;
        }
    });
    Object.keys(tempObject).length && result.push(tempObject);
});

console.log(result);

Upvotes: 0

Ja9ad335h
Ja9ad335h

Reputation: 5075

your requirement(result) is not clear, but this will get you started.

var arr1 = [{ "329": ["45738", "45737", "45736"] }, { "307": ["45467", "45468"] }, { "355": ["47921"] }],
  arr2 = [{ "355": ["47921", "45922"] }, { "329": ["45738", "45737", "45736"] }, { "307": [] }];

var result = [];

arr2.forEach(obj => {
  var key = Object.keys(obj)[0];
  var match = arr1.find(o => o.hasOwnProperty(key));
  if (match) {
    var newObj = {};
    newObj[key] = obj[key].filter(s => match[key].indexOf(s) === -1);
    if (!obj[key].length || newObj[key].length) result.push(newObj)
  } else {
    result.push(Object.assign({}, obj));
  }
});

console.log(result);

Upvotes: 1

Related Questions