我有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的东西…
当前回答
这是一个简单的带有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;
};
其他回答
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]
“一族”许可
对于无意中发现这条线索的人,这里有一个更完整的解决方案。它将比较两个对象,并给出所有属性的键,这些属性要么只存在于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]。
已经有很多答案发布,但对于那些好奇的人来说,避免编写任何代码来计算具有任何类型结构的两个对象之间的差异,实际上有一个库可以做到这一点。Lodash isEqual只返回true或false,它不返回任何关于更改属性的信息。https://www.npmjs.com/package/deep-diff
它返回两个对象之间差异的完整细节
import DeepDiff from 'deep-diff';
let a = {...} //some object
let b = {...} //some object
var differences = DeepDiff.diff(a, b);
在这篇文章中也有人提出了类似的问题 获取2个JSON对象之间的差异
我们需要在两个json更新之间获取delta,以跟踪数据库更新。也许其他人会觉得这很有用。
https://gist.github.com/jp6rt/7fcb6907e159d7851c8d59840b669e3d
const {
isObject,
isEqual,
transform,
has,
merge,
} = require('lodash');
const assert = require('assert');
/**
* Perform a symmetric comparison on JSON object.
* @param {*} baseObj - The base object to be used for comparison against the withObj.
* @param {*} withObj - The withObject parameter is used as the comparison on the base object.
* @param {*} invert - Because this is a symmetric comparison. Some values in the with object
* that doesn't exist on the base will be lost in translation.
* You can execute again the function again with the parameters interchanged.
* However you will lose the reference if the value is from the base or with
* object if you intended to do an assymetric comparison.
* Setting this to true will do make sure the reference is not lost.
* @returns - The returned object will label the result of the comparison with the
* value from base and with object.
*/
const diffSym = (baseObj, withObj, invert = false) => transform(baseObj, (result, value, key) => {
if (isEqual(value, withObj[key])
&& has(withObj, key)) {
return;
}
if (isObject(value)
&& isObject(withObj[key])
&& !Array.isArray(value)) {
result[key] = diffSym(value, withObj[key], invert);
return;
}
if (!invert) {
result[key] = {
base: value,
with: withObj[key],
};
return;
}
if (invert) {
result[key] = {
base: withObj[key],
with: value,
};
}
});
/**
* Perform a assymmetric comparison on JSON object.
* @param {*} baseObj - The base object to be used for comparison against the withObj.
* @param {*} withObj - The withObject parameter is used as the comparison on the base object.
* @returns - The returned object will label the values with
* reference to the base and with object.
*/
const diffJSON = (baseObj, withObj) => {
// Deep clone the objects so we don't update the reference objects.
const baseObjClone = JSON.parse(JSON.stringify(baseObj));
const withObjClone = JSON.parse(JSON.stringify(withObj));
const beforeDelta = diffSym(baseObjClone, withObjClone);
const afterDelta = diffSym(withObjClone, baseObjClone, true);
return merge(afterDelta, beforeDelta);
};
// By Example:
const beforeDataObj = {
a: 1,
c: { d: 2, f: 3 },
g: 4,
h: 5,
};
const afterDataObj = {
a: 2,
b: 3,
c: { d: 1, e: 1 },
h: 5,
};
const delta = diffJSON(beforeDataObj, afterDataObj);
// Assert expected result.
assert(isEqual(delta, {
a: { base: 1, with: 2 },
b: { base: undefined, with: 3 },
c: {
d: { base: 2, with: 1 },
e: { base: undefined, with: 1 },
f: { base: 3, with: undefined },
},
g: { base: 4, with: undefined },
}));
这是一个简单的带有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;
};