如何做两个对象与lodash深入的比较?

我有2个不同的嵌套对象,我需要知道他们是否有一个嵌套属性的差异。

var a = {}; var b = {}; a.prop1 = 2; a.prop2 = { prop3: 2 }; b.prop1 = 2; b.prop2 = { prop3: 3 }; 

该对象可能会更复杂,更多的嵌套属性。 但是这是一个很好的例子。 我可以select使用recursion函数或lodash的东西…

一个简单而优雅的解决scheme是使用_.isEqual ,它进行深入的比较:

 var a = {}; var b = {}; a.prop1 = 2; a.prop2 = { prop3: 2 }; b.prop1 = 2; b.prop2 = { prop3: 3 }; _.isEqual(a, b); // returns false if different 

但是,此解决scheme不显示哪个属性是不同的。

http://jsfiddle.net/bdkeyn0h/

如果您需要知道哪些属性不同,请使用reduce() :

 _.reduce(a, function(result, value, key) { return _.isEqual(value, b[key]) ? result : result.concat(key); }, []); // → [ "prop2" ] 

这里有一个简洁的解决scheme:

 _.differenceWith(a, b, _.isEqual); 

对于任何人在这个线程磕磕绊绊,这是一个更完整的解决scheme。 它将比较两个对象,并为您提供所有属性的关键字,这些属性仅在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,则可以用_.isEqualreplace正常值比较,例如obj1[key] === obj2[key]

根据Adam Boduch的回答 ,我写了这个函数,它比较两个对象的最深层意义 ,返回具有不同值的path以及从一个或另一个对象中缺less的path。

代码没有高效率地编写,这方面的改进是非常受欢迎的,但这里是基本的forms:

 var compare = function (a, b) { var result = { different: [], missing_from_first: [], missing_from_second: [] }; _.reduce(a, function (result, value, key) { if (b.hasOwnProperty(key)) { if (_.isEqual(value, b[key])) { return result; } else { if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) { //dead end. result.different.push(key); return result; } else { var deeper = compare(a[key], b[key]); result.different = result.different.concat(_.map(deeper.different, (sub_path) => { return key + "." + sub_path; })); result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => { return key + "." + sub_path; })); result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => { return key + "." + sub_path; })); return result; } } } else { result.missing_from_second.push(key); return result; } }, result); _.reduce(b, function (result, value, key) { if (a.hasOwnProperty(key)) { return result; } else { result.missing_from_first.push(key); return result; } }, result); return result; } 

您可以使用此代码段来尝试代码(build议在完整页面模式下运行):

 var compare = function (a, b) { var result = { different: [], missing_from_first: [], missing_from_second: [] }; _.reduce(a, function (result, value, key) { if (b.hasOwnProperty(key)) { if (_.isEqual(value, b[key])) { return result; } else { if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) { //dead end. result.different.push(key); return result; } else { var deeper = compare(a[key], b[key]); result.different = result.different.concat(_.map(deeper.different, (sub_path) => { return key + "." + sub_path; })); result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => { return key + "." + sub_path; })); result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => { return key + "." + sub_path; })); return result; } } } else { result.missing_from_second.push(key); return result; } }, result); _.reduce(b, function (result, value, key) { if (a.hasOwnProperty(key)) { return result; } else { result.missing_from_first.push(key); return result; } }, result); return result; } var a_editor = new JSONEditor($('#a')[0], { name: 'a', mode: 'code' }); var b_editor = new JSONEditor($('#b')[0], { name: 'b', mode: 'code' }); var a = { same: 1, different: 2, missing_from_b: 3, missing_nested_from_b: { x: 1, y: 2 }, nested: { same: 1, different: 2, missing_from_b: 3 } } var b = { same: 1, different: 99, missing_from_a: 3, missing_nested_from_a: { x: 1, y: 2 }, nested: { same: 1, different: 99, missing_from_a: 3 } } a_editor.set(a); b_editor.set(b); var result_editor = new JSONEditor($('#result')[0], { name: 'result', mode: 'view' }); var do_compare = function() { var a = a_editor.get(); var b = b_editor.get(); result_editor.set(compare(a, b)); } 
 #objects {} #objects section { margin-bottom: 10px; } #objects section h1 { background: #444; color: white; font-family: monospace; display: inline-block; margin: 0; padding: 5px; } .jsoneditor-outer, .ace_editor { min-height: 230px !important; } button:hover { background: orangered; } button { cursor: pointer; background: red; color: white; text-align: left; font-weight: bold; border: 5px solid crimson; outline: 0; padding: 10px; margin: 10px 0px; } 
 <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.css" rel="stylesheet" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.5.10/jsoneditor.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="objects"> <section> <h1>a (first object)</h1> <div id="a"></div> </section> <section> <h1>b (second object)</h1> <div id="b"></div> </section> <button onClick="do_compare()">compare</button> <section> <h1>result</h1> <div id="result"></div> </section> </div> 

此代码返回一个对象,其中包含具有不同值的所有属性以及两个对象的值。 有用的logging差异。

 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; }, {}); 

如果您只需要关键比较:

  _.reduce(a, function(result, value, key) { return b[key] === undefined ? key : [] }, []); 

我刺了一个亚当Boduch的代码输出一个深刻的差异 – 这是完全未经testing,但件在那里:

 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"] 

使用(嵌套)属性的模板进行深入比较来检查

 function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) { if (!objectA || !objectB) return false let areDifferent = false Object.keys(comparisonTemplate).some((key) => { if (typeof comparisonTemplate[key] === 'object') { areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key]) return areDifferent } else if (comparisonTemplate[key] === true) { areDifferent = objectA[key] !== objectB[key] return areDifferent } else { return false } }) return !areDifferent } const objA = { a: 1, b: { a: 21, b: 22, }, c: 3, } const objB = { a: 1, b: { a: 21, b: 25, }, c: true, } // template tells which props to compare const comparisonTemplateA = { a: true, b: { a: true } } objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA) // returns true const comparisonTemplateB = { a: true, c: true } // returns false objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB) 

这将在控制台中工作。 可以根据需要添加arrays支持

完成Adam Boduch的答案之后,这个问题就有了不同的属性

 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 /下划线,我已经写了这个代码,并正在为我工​​作很好比较object1 object2

 function getObjectDiff(a, b) { var diffObj = {}; if (Array.isArray(a)) { a.forEach(function(elem, index) { if (!Array.isArray(diffObj)) { diffObj = []; } diffObj[index] = getObjectDiff(elem, (b || [])[index]); }); } else if (a != null && typeof a == 'object') { Object.keys(a).forEach(function(key) { if (Array.isArray(a[key])) { var arr = getObjectDiff(a[key], b[key]); if (!Array.isArray(arr)) { arr = []; } arr.forEach(function(elem, index) { if (!Array.isArray(diffObj[key])) { diffObj[key] = []; } diffObj[key][index] = elem; }); } else if (typeof a[key] == 'object') { diffObj[key] = getObjectDiff(a[key], b[key]); } else if (a[key] != (b || {})[key]) { diffObj[key] = a[key]; } else if (a[key] == (b || {})[key]) { delete a[key]; } }); } Object.keys(diffObj).forEach(function(key) { if (typeof diffObj[key] == 'object' && JSON.stringify(diffObj[key]) == '{}') { delete diffObj[key]; } }); return diffObj; } 
 var isEqual = function(f,s) { if (f === s) return true; if (Array.isArray(f)&&Array.isArray(s)) { return isEqual(f.sort(), s.sort()); } if (_.isObject(f)) { return isEqual(f, s); } return _.isEqual(f, s); };