Compare two arrays containing objects in order to calculate what changed?

岁酱吖の 提交于 2019-12-22 08:48:06

问题


Using flat JavaScript or by making use of lodash, what is the simplest way (hoping lodash has a function) which I compare the following arrays and return the value which has changed:

Before

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

After

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

So between before and after, Frank is now age 33, so how can I simply return:

{id: 1, name: 'Frank', age: 33}

Or a more desired result:

{id: 1, age: 33}

EDIT:

As I got such a nice variation of answers to my question, I decided to test them all on server and client side. Here is what I got after using json-generator to generate a json file of 10 000 records:

Node 7.1.0:

David Domain. (Flat JS filter & some): 3.396
Result: { id: 1, name: 'Frank', age: 33 }
Ben Aston (Flat JS nested itteration): 4.359
Result: { age: 33, id: 1 }
Gille Q. (Lodash reduce): 21.335
Result: { id: 1, age: 33 }
Stasovlas. (Lodash differenceBy): 1.442
Result: []  
Vignesh Murugan. (Lodash findWhere): 0
Result: _.findWhere is not a function

Firefox 50.0.2:

David Domain. (Flat JS filter & some): 6.695
Result: { id: 1, name: 'Frank', age: 33 }
Ben Aston (Flat JS nested itteration): 10.594
Result: { age: 33, id: 1 }
Gille Q. (Lodash reduce): 40.085
Result: { id: 1, age: 33 }
Stasovlas. (Lodash differenceBy): 6.499
Result: []

The interesting thing to note here is that Lodash differenceBy does not seem to work when you're dealing with larger amounts of data, at best i could get this to work with only 3 records before i gave up.

@Vignesh must have worked at one point with Underscore but i'm not going to cover this as things have changed and we now use Lodash.

Here is the code I used to test, used timely to track the amount of time taken to execute a function, then looped 1000 times to get the total time to execute the function 1000 times then divided by 1000 to get the average amount of time taken (in ms) to execute the function:

var fs = require('fs');
var timely = require('timely');
var _ = require('lodash');

// Ben Aston
var ben_aston = function (a, b) {
  return a.reduce((p,c,i)=>{
    var diff = objDiff(c, b[i]);
    diff && p.push(diff);
    return p;
  }, [])
}
function objDiff(a, b) {
  var diff = Object.keys(a).reduce((p,c,i)=>{
    if (a[c] === b[c]) {
      return p;
    }
    p[c] = b[c];
    return p;
  }, {});
  if (!Object.keys(diff).length) {
    return;
  }
  diff.id = a.id;
  return diff;
}
var ben_astonT = timely(ben_aston);


// Gille Q.
var gille_q = function (before, after) {
  return _.reduce(before, function(result, value, key) {
    return _.isEqual(value, after[key]) ?
    result : result.concat({id: after[key].id, age: after[key].age});
  }, []);
}
var gille_qT = timely(gille_q);


// David Domain
var david_domain = function (before, after) {
  return after.filter( function( p, idx ) {
    return Object.keys(p).some( function( prop ) {
      return p[prop] !== before[idx][prop];
    })
  })
}
var david_domainT = timely(david_domain);


// Stasovlas
var stasovlas = function (before, after) {
  return _.differenceBy(after, before, 'age');
}
var stasovlasT = timely(stasovlas);


// Vignesh Murugan
var vignesh_murugan = function (before, after) {
  before.forEach((current) => {
    var after = _.findWhere(after,{id : current.id});
    if(!_.isEqual(after , current)) {
      return _.pick(after,"id","name");
    }
  });
}
var vignesh_muruganT = timely(vignesh_murugan);


// Load the data
var before = JSON.parse(fs.readFileSync('./before.json', 'utf8'));
var after = JSON.parse(fs.readFileSync('./after.json', 'utf8'));

// Open average tracking
var ben_aston_ave = 0,
    gille_q_ave = 0,
    david_domain_ave = 0,
    stasovlas_ave = 0,
    vignesh_murugan_ave = 0;

// Do test
for (i = 0; i < 1000; i++) {
  // Ben Aston
  ben_astonT(before, after);
  ben_aston_ave += ben_astonT.time;

  // Gille Q.
  gille_qT(before, after);
  gille_q_ave += gille_qT.time;

  // David Domain
  david_domainT(before, after);
  david_domain_ave += david_domainT.time;

  // Stasovlas
  stasovlasT(before, after);
  stasovlas_ave += stasovlasT.time;

  // Vignesh Murugan
  // vignesh_muruganT(before, after);
  // vignesh_murugan_ave += vignesh_muruganT.time;
}

// Calc averages
ben_aston_ave = ben_aston_ave / 1000;
gille_q_ave = gille_q_ave / 1000;
david_domain_ave = david_domain_ave / 1000;
stasovlas_ave = stasovlas_ave / 1000;
vignesh_murugan_ave = vignesh_murugan_ave / 1000;


console.log('David Domain. (Flat JS filter & some): '+david_domain_ave);
console.log('Result: { id: 1, name: \'Frank\', age: 33 }');
console.log('Ben Aston (Flat JS nested itteration): '+ben_aston_ave);
console.log('Result: { age: 33, id: 1 }');
console.log('Gille Q. (Lodash reduce): '+gille_q_ave);
console.log('Result: { id: 1, age: 33 }');
console.log('Stasovlas. (Lodash differenceBy): '+stasovlas_ave);
console.log('Result: []');
console.log('Vignesh Murugan. (Lodash findWhere): '+vignesh_murugan_ave);
console.log('Result: _.findWhere is not a function');

回答1:


You could use Array.filter with Array.some, which will give you a new Array with the changed items.

Maybe something like so:

var before = [
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

var after = [
  {id: 0, name: 'Bobb', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

var changed = after.filter( function( p, idx ) {
  return Object.keys(p).some( function( prop ) {
    return p[prop] !== before[idx][prop];
  })
})

console.log(changed)
.as-console-wrapper {
  max-height: 100% !important;
}



回答2:


use _.differenceBy

var res = _.differenceBy(after, before, 'age');



回答3:


You can also use lodash reduce method for comparison, here is the code I made for your example wich return what you want in this jsfiddle:

https://jsfiddle.net/7rf9bphL/1/

var a = [
    {id: 0, name: 'Bob', age: 27},
    {id: 1, name: 'Frank', age: 32},
    {id: 2, name: 'Joe', age: 38}];

var b = [
    {id: 0, name: 'Bob', age: 27},
    {id: 1, name: 'Frank', age: 33},
    {id: 2, name: 'Joe', age: 38}];

var result = _.reduce(a, function(result, value, key) {
    return _.isEqual(value, b[key]) ?
        result : result.concat({id: b[key].id, age: b[key].age});
}, []);
console.log("result", result);



回答4:


Assumes array indexing remains the same:

function diff(a, b) {
    return a.reduce((p,c,i)=>{
        var diff = objDiff(c, b[i]);
        diff && p.push(diff);
        return p;
    }, [])
}
function objDiff(a, b) {
    var diff = Object.keys(a).reduce((p,c,i)=>{
        if (a[c] === b[c]) {
            return p;
        }
        p[c] = b[c];
        return p;
    }, {});
    if (!Object.keys(diff).length) {
        return;
    }
    diff.id = a.id;
    return diff;
}

const before = [{
    id: 0, name: 'Bob', age: 27 }, {
    id: 1, name: 'Frank', age: 32 }, {
    id: 2, name: 'Joe', age: 38 }]
const after = [{
    id: 0, name: 'Bob', age: 27 }, {
    id: 1, name: 'Frank', age: 33 }, {
    id: 2, name: 'Joe', age: 38 }];

console.log(diff(before, after));



回答5:


I tried using underscore and it works fine

  var x = [
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

var y = [
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

  x.forEach((current) => {
    var after = _.findWhere(y,{id : current.id})
    if(!_.isEqual(after , current)){
      console.log(_.pick(after,"id","name"))
    }
  })

Here is the Lodash Solution

var difference = [];

x.forEach((current) => {
  var after = _.find(y,{id : current.id})
  if(!_.isEqual(after , current)){
    difference.push(_.pick(after,"id","name"))
  }
})

console.log(difference)


来源:https://stackoverflow.com/questions/41122068/compare-two-arrays-containing-objects-in-order-to-calculate-what-changed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!