我有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的东西…
当前回答
As it was asked, here's a recursive object comparison function. And a bit more. Assuming that primary use of such function is object inspection, I have something to say. Complete deep comparison is a bad idea when some differences are irrelevant. For example, blind deep comparison in TDD assertions makes tests unnecessary brittle. For that reason, I'd like to introduce a much more valuable partial diff. It is a recursive analogue of a previous contribution to this thread. It ignores keys not present in a
var bdiff = (a, b) =>
_.reduce(a, (res, val, key) =>
res.concat((_.isPlainObject(val) || _.isArray(val)) && b
? bdiff(val, b[key]).map(x => key + '.' + x)
: (!b || val != b[key] ? [key] : [])),
[]);
BDiff允许检查期望值,同时容忍其他属性,这正是您想要的自动检查。这允许构建各种高级断言。例如:
var diff = bdiff(expected, actual);
// all expected properties match
console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
// controlled inequality
console.assert(diff.length < 3, "Too many differences", diff, expected, actual);
回到完整的解决方案。使用bdiff构建一个完整的传统diff是很简单的:
function diff(a, b) {
var u = bdiff(a, b), v = bdiff(b, a);
return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
.concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
.concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
};
在两个复杂对象上运行上述函数将输出类似于下面的内容:
[
" < components.0.components.1.components.1.isNew",
" < components.0.cryptoKey",
" | components.0.components.2.components.2.components.2.FFT.min",
" | components.0.components.2.components.2.components.2.FFT.max",
" > components.0.components.1.components.1.merkleTree",
" > components.0.components.2.components.2.components.2.merkleTree",
" > components.0.components.3.FFTResult"
]
最后,为了了解值之间的差异,我们可能需要直接eval() diff输出。为此,我们需要一个更丑的bdiff版本,输出语法正确的路径:
// provides syntactically correct output
var bdiff = (a, b) =>
_.reduce(a, (res, val, key) =>
res.concat((_.isPlainObject(val) || _.isArray(val)) && b
? bdiff(val, b[key]).map(x =>
key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
: (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
[]);
// now we can eval output of the diff fuction that we left unchanged
diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));
这将输出类似于下面的内容:
[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
“一族”许可
其他回答
这段代码返回一个具有不同值的所有属性的对象,以及两个对象的值。对记录差异很有用。
var allkeys = _.union(_.keys(obj1), _.keys(obj2));
var difference = _.reduce(allkeys, function (result, key) {
if ( !_.isEqual(obj1[key], obj2[key]) ) {
result[key] = {obj1: obj1[key], obj2: obj2[key]}
}
return result;
}, {});
这是一个简单的带有Lodash深度差异检查器的Typescript,它将生成一个新对象,只包含旧对象和新对象之间的差异。
例如,如果我们有:
const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};
结果对象将是:
const result: {b: 3};
它还兼容多层深层对象,对于数组,它可能需要一些调整。
import * as _ from "lodash";
export const objectDeepDiff = (data: object | any, oldData: object | any) => {
const record: any = {};
Object.keys(data).forEach((key: string) => {
// Checks that isn't an object and isn't equal
if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
record[key] = data[key];
}
// If is an object, and the object isn't equal
if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
record[key] = objectDeepDiff(data[key], oldData[key]);
}
});
return record;
};
我需要知道它们的某个嵌套属性是否不同
其他答案为这个问题提供了可能令人满意的解决方案,但它非常困难和常见,似乎有一个非常受欢迎的包来帮助解决这个问题。
要使用这个包,你需要npm i deep-object-diff,然后:
const { diff } = require('deep-object-diff');
var a = {};
var b = {};
a.prop1 = 2;
a.prop2 = { prop3: 2 };
b.prop1 = 2;
b.prop2 = { prop3: 3 };
if (!_.isEqual(a, b)) {
const abDiff = diff(a, b);
console.log(abDiff);
/*
{
prop2: {
prop3: 3
}
}
*/
}
// or alternatively
const abDiff = diff(a, b);
if(!_.isEmpty(abDiff)) {
// if a diff exists then they aren't deeply equal
// perform needed actions with diff...
}
下面是一个更详细的案例,直接从他们的文档中删除属性:
const lhs = {
foo: {
bar: {
a: ['a', 'b'],
b: 2,
c: ['x', 'y'],
e: 100 // deleted
}
},
buzz: 'world'
};
const rhs = {
foo: {
bar: {
a: ['a'], // index 1 ('b') deleted
b: 2, // unchanged
c: ['x', 'y', 'z'], // 'z' added
d: 'Hello, world!' // added
}
},
buzz: 'fizz' // updated
};
console.log(diff(lhs, rhs)); // =>
/*
{
foo: {
bar: {
a: {
'1': undefined
},
c: {
'2': 'z'
},
d: 'Hello, world!',
e: undefined
}
},
buzz: 'fizz'
}
*/
有关实现细节和其他使用信息,请参阅该repo。
作为对亚当·博杜赫的回答的补充,这个问题考虑到了性质的差异
const differenceOfKeys = (...objects) =>
_.difference(...objects.map(obj => Object.keys(obj)));
const differenceObj = (a, b) =>
_.reduce(a, (result, value, key) => (
_.isEqual(value, b[key]) ? result : [...result, key]
), differenceOfKeys(b, a));
下面是一个使用Lodash的简单解决方案:
_.differenceWith(a, b, _.isEqual);
注意,两个输入都需要是数组(可能是一个对象的数组)。