比较两个包含对象的数组以计算发生了啥变化?
Posted
技术标签:
【中文标题】比较两个包含对象的数组以计算发生了啥变化?【英文标题】:Compare two arrays containing objects in order to calculate what changed?比较两个包含对象的数组以计算发生了什么变化? 【发布时间】:2017-04-28 13:42:35 【问题描述】:使用平面 javascript 或通过使用 lodash,我比较以下数组并返回已更改的值的最简单方法是什么(希望 lodash 有一个函数):
之前
[
id: 0, name: 'Bob', age: 27,
id: 1, name: 'Frank', age: 32,
id: 2, name: 'Joe', age: 38
]
之后
[
id: 0, name: 'Bob', age: 27,
id: 1, name: 'Frank', age: 33,
id: 2, name: 'Joe', age: 38
]
所以在前后之间,弗兰克现在已经 33 岁了,那我怎么能简单地返回:
id: 1, name: 'Frank', age: 33
或者更理想的结果:
id: 1, age: 33
编辑:
由于我的问题得到了如此多的答案,我决定在服务器端和客户端对它们进行全部测试。这是我使用json-generator 生成包含 10 000 条记录的 json 文件后得到的结果:
节点 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: []
这里要注意的有趣的是,当您处理大量数据时,Lodash differenceBy 似乎不起作用,在我放弃之前我最多只能处理 3 条记录。
@Vignesh 一定曾经使用过 Underscore,但我不打算介绍这一点,因为事情已经发生了变化,我们现在使用 Lodash。
这是我用来测试的代码,用于及时跟踪执行函数所花费的时间,然后循环 1000 次得到执行函数的总时间 1000 次然后除以 1000 得到平均时间执行函数所花费的时间(以毫秒为单位):
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');
【问题讨论】:
您可以发布您尝试过的内容吗? @SankarRaj 我尝试获取结果并与之前的数组进行比较,但这不是一个好主意,因为数据量很大。不想通过为此提供代码来向任何一个方向推送任何可能的答案,因为我想看看有哪些选项可用。也许只是因为缺少正确的术语,我不确定? ***.com/questions/13147278/… 或其他类似问题之一 物体会比这更深吗?如果不是,您可以在将它们字符串化后尝试进行字符串比较 @Goliadkin 不,他们总是这样,但这是一个聪明的主意。我认为它也会很快。我会考虑一下并尝试一下,谢谢! 【参考方案1】:我试过用下划线,效果很好
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"))
)
这是 Lodash 解决方案
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)
【讨论】:
您好,请查看我的问题的编辑,发布了一些您可能会感兴趣的基准测试结果。您的解决方案没有放在我的基准测试中,因为它具有 Underscore.js 并且有问题的功能在 Lodash 中不再有效。【参考方案2】:假设数组索引保持不变:
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));
【讨论】:
谢谢 Ben,非常感谢您在这里的时间和精力,我也想知道这种方法有什么名字吗?我想这就是 lodash 在下面所做的。对所有这些示例进行基准测试,看看哪个是最快的,只是为了好玩:) 这实际上是一个嵌套迭代。不知道有没有更好的形容词。 请注意,此解决方案假定数组中的位置很重要。 您好,请查看我的问题的编辑,发布了一些您可能会感兴趣的基准测试结果。您的解决方案排名第二。【参考方案3】:您也可以使用 lodash reduce 方法进行比较,这是我为您的示例制作的代码,它在此 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);
【讨论】:
呵呵,我实际上也尝试过使用 reduce 来做到这一点,但没有成功,现在可以看到原因了。谢谢,感谢您的时间和精力,将对示例进行基准测试,看看哪个最快! @CraigvanTonder 总是乐于提供帮助,不客气! 您好,请查看我的问题的编辑,发布了一些您可能会感兴趣的基准测试结果。您的解决方案排名第三。 是的,这很有趣,reduce 对于大量数据并不是很强大,感谢您的测试:)【参考方案4】:您可以将Array.filter
与Array.some
一起使用,这将为您提供一个包含已更改项目的新数组。
可能是这样的:
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;
【讨论】:
谢谢,这几乎是我自己尝试做的,但无法做到。我感谢答案,因为它帮助我学习!我将对提供的示例进行基准测试,看看哪个是最快的:) 当然,没问题。请注意,这只是普通的 JavaScript,不需要 lodash。 我很感激,是的,这使它成为我心目中更好的解决方案。只有少数地方我使用了 lodash,这是因为在这一点上得出结论的方法有点超出我的范围。看到做其他复杂事情的简单方法总是有帮助的,这确实是我可以理解和使用的一种,lodash的-1用例,也许有一天我根本不需要它:) 您好,请查看我的问题的编辑,发布了一些您可能会感兴趣的基准测试结果。您的解决方案排名第一。【参考方案5】:使用_.differenceBy
var res = _.differenceBy(after, before, 'age');
【讨论】:
非常感谢,这看起来像是赢家,但我将对提供的示例进行基准测试,看看哪个是最快的:) 您好,请查看我的问题的编辑,发布了一些您可能会感兴趣的基准测试结果。您的解决方案没有放在我的基准测试中,因为它不适用于更大的数据集。奇怪但真实的事情,除非你能证明否则我相信大卫对这个问题有最好的答案? @CraigvanTonder 干得好!嗯,这真的很奇怪,我认为最好问问大卫。并将他的答案也分享给我,请以上是关于比较两个包含对象的数组以计算发生了啥变化?的主要内容,如果未能解决你的问题,请参考以下文章