Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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');
like image 937
Craig van Tonder Avatar asked Mar 11 '23 10:03

Craig van Tonder


1 Answers

use _.differenceBy

var res = _.differenceBy(after, before, 'age');
like image 75
stasovlas Avatar answered Mar 13 '23 01:03

stasovlas