我有2个不同的嵌套对象,我需要知道它们是否在其中一个嵌套属性中有不同。
var a = {};
var b = {};
a.prop1 = 2;
a.prop2 = { prop3: 2 };
b.prop1 = 2;
b.prop2 = { prop3: 3 };
对象可以更复杂,有更多嵌套的属性。但这是一个很好的例子。我可以选择使用递归函数或lodash的东西…
我有2个不同的嵌套对象,我需要知道它们是否在其中一个嵌套属性中有不同。
var a = {};
var b = {};
a.prop1 = 2;
a.prop2 = { prop3: 2 };
b.prop1 = 2;
b.prop2 = { prop3: 3 };
对象可以更复杂,有更多嵌套的属性。但这是一个很好的例子。我可以选择使用递归函数或lodash的东西…
当前回答
我尝试了Adam Boduch的代码来输出一个很深的差异-这完全没有经过测试,但碎片在那里:
function diff (obj1, obj2, path) {
obj1 = obj1 || {};
obj2 = obj2 || {};
return _.reduce(obj1, function(result, value, key) {
var p = path ? path + '.' + key : key;
if (_.isObject(value)) {
var d = diff(value, obj2[key], p);
return d.length ? result.concat(d) : result;
}
return _.isEqual(value, obj2[key]) ? result : result.concat(p);
}, []);
}
diff({ foo: 'lol', bar: { baz: true }}, {}) // returns ["foo", "bar.baz"]
其他回答
这是基于@JLavoie,使用lodash
let differences = function (newObj, oldObj) {
return _.reduce(newObj, function (result, value, key) {
if (!_.isEqual(value, oldObj[key])) {
if (_.isArray(value)) {
result[key] = []
_.forEach(value, function (innerObjFrom1, index) {
if (_.isNil(oldObj[key][index])) {
result[key].push(innerObjFrom1)
} else {
let changes = differences(innerObjFrom1, oldObj[key][index])
if (!_.isEmpty(changes)) {
result[key].push(changes)
}
}
})
} else if (_.isObject(value)) {
result[key] = differences(value, oldObj[key])
} else {
result[key] = value
}
}
return result
}, {})
}
https://jsfiddle.net/EmilianoBarboza/0g0sn3b9/8/
一个简单而优雅的解决方案是使用_。isEqual,它执行深度比较:
Var a = {}; Var b = {}; A.prop1 = 2; A.prop2 = {prop3: 2}; B.prop1 = 2; B.prop2 = {prop3: 3}; console.log(_。isEqual (a, b));//如果不同则返回false < script src = " https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js " > < /脚本>
然而,这个解决方案并没有显示哪个属性是不同的。
对于无意中发现这条线索的人,这里有一个更完整的解决方案。它将比较两个对象,并给出所有属性的键,这些属性要么只存在于object1中,要么只存在于object2中,要么同时存在于object1和object2中,但值不同:
/*
* Compare two objects by reducing an array of keys in obj1, having the
* keys in obj2 as the intial value of the result. Key points:
*
* - All keys of obj2 are initially in the result.
*
* - If the loop finds a key (from obj1, remember) not in obj2, it adds
* it to the result.
*
* - If the loop finds a key that are both in obj1 and obj2, it compares
* the value. If it's the same value, the key is removed from the result.
*/
function getObjectDiff(obj1, obj2) {
const diff = Object.keys(obj1).reduce((result, key) => {
if (!obj2.hasOwnProperty(key)) {
result.push(key);
} else if (_.isEqual(obj1[key], obj2[key])) {
const resultKeyIndex = result.indexOf(key);
result.splice(resultKeyIndex, 1);
}
return result;
}, Object.keys(obj2));
return diff;
}
下面是一个输出示例:
// Test
let obj1 = {
a: 1,
b: 2,
c: { foo: 1, bar: 2},
d: { baz: 1, bat: 2 }
}
let obj2 = {
b: 2,
c: { foo: 1, bar: 'monkey'},
d: { baz: 1, bat: 2 }
e: 1
}
getObjectDiff(obj1, obj2)
// ["c", "e", "a"]
如果你不关心嵌套对象并且想要跳过lodash,你可以替换_。isEqual用于正常的值比较,例如obj1[key] === obj2[key]。
这是我对这个问题的解决办法
const _ = require('lodash');
var objects = [{ 'x': 1, 'y': 2, 'z':3, a:{b:1, c:2, d:{n:0}}, p:[1, 2, 3] }, { 'x': 2, 'y': 1, z:3, a:{b:2, c:2,d:{n:1}}, p:[1,3], m:3 }];
const diffFn=(a,b, path='')=>_.reduce(a, function(result, value, key) {
if(_.isObjectLike(value)){
if(_.isEqual(value, b[key])){
return result;
}else{
return result.concat(diffFn(value, b[key], path?(`${path}.${key}`):key))
}
}else{
return _.isEqual(value, b[key]) ?
result : result.concat(path?(`${path}.${key}`):key);
}
}, []);
const diffKeys1=diffFn(objects[0], objects[1])
const diffKeys2=diffFn(objects[1], objects[0])
const diffKeys=_.union(diffKeys1, diffKeys2)
const res={};
_.forEach(diffKeys, (key)=>_.assign(res, {[key]:{ old: _.get(objects[0], key), new:_.get(objects[1], key)} }))
res
/*
Returns
{
x: { old: 1, new: 2 },
y: { old: 2, new: 1 },
'a.b': { old: 1, new: 2 },
'a.d.n': { old: 0, new: 1 },
'p.1': { old: 2, new: 3 },
'p.2': { old: 3, new: undefined },
m: { old: undefined, new: 3 }
}
*/
如果你需要知道哪些属性是不同的,使用reduce():
_.reduce(a, function(result, value, key) {
return _.isEqual(value, b[key]) ?
result : result.concat(key);
}, []);
// → [ "prop2" ]