如何用JS或Lodash获取含数组的嵌套对象深度差异
问题描述
我有以下两个嵌套对象:
obj1
obj1 = { sofa: [ { name: 'leather', typeSofa: [ { name: 'type1', text: 'type1', quantity: 1 }, { name: 'type2', text: 'type2', quantity: 1 } ], stool: [ { name: 'type1', text: 'type1', quantity: 1 }, { name: 'type2', text: 'type2', quantity: 1 } ] }, { name: 'fabric', typeSofa: [ { name: 'type1', text: 'type1', quantity: 1 } ], stool: [ { name: 'type1', text: 'type1', quantity: 1 }, { name: 'type2', text: 'type2', quantity: 1 } ] } ], curtain: { dryclean: [{ name: 'text1', text: 'text1', quantity: 1 }] } }
obj2
仅将sofa下leather的type1的quantity改为2:
obj2 = { sofa: [ { name: 'leather', typeSofa: [ { name: 'type1', text: 'type1', quantity: 2 }, { name: 'type2', text: 'type2', quantity: 1 } ], stool: [ { name: 'type1', text: 'type1', quantity: 1 }, { name: 'type2', text: 'type2', quantity: 1 } ] }, { name: 'fabric', typeSofa: [ { name: 'type1', text: 'type1', quantity: 1 } ], stool: [ { name: 'type1', text: 'type1', quantity: 1 }, { name: 'type2', text: 'type2', quantity: 1 } ] } ], curtain: { dryclean: [ { name: 'text1', text: 'text1', quantity: 1 } ] } }
我使用以下基于Lodash的JS函数检测对象差异:
const findDifferences = (obj1, obj2) => { // Find the keys that are in both objects const keys = _.intersection(Object.keys(obj1), Object.keys(obj2)); // Iterate through the keys return keys.reduce((diff, key) => { const val1 = obj1[key]; const val2 = obj2[key]; // If the values are arrays, compare them using _.differenceWith() and recursion if (_.isArray(val1) && _.isArray(val2)) { diff[key] = _.differenceWith(val2, val1, _.isEqual); if (diff[key].length === 0) delete diff[key]; } // If the values are objects, recursively call findDifferences() on them else if (_.isObject(val1) && _.isObject(val2)) { const nestedDiff = findDifferences(val1, val2); if (!_.isEmpty(nestedDiff)) diff[key] = nestedDiff; } // Otherwise, compare the values directly using _.isEqual() else if (!_.isEqual(val1, val2)) { diff[key] = val2; } return diff; }, {}); };
当前函数在修改curtain的quantity时能正常识别差异,但无法识别sofa数组内嵌套属性的变更。我期望得到如下格式的差异结果:
result = { sofa: [ { name: 'leather', typeSofa: [ { name: 'type1', text: 'type1', quantity: 2 } ] } ] }
请问如何修改函数或通过其他方式实现该需求?
解决方案
原函数处理数组时,_.differenceWith(val2, val1, _.isEqual)仅能找出obj2中完全不存在于obj1的数组元素,但你的场景是数组内对象的部分属性修改,并非新增/删除元素,因此该逻辑不适用。
要实现需求,需遍历数组中通过标识键(如name)匹配的元素对,递归比较差异并仅保留变化部分。以下是修改后的函数:
const findDifferences = (obj1, obj2) => { // 按name匹配数组元素,递归查找差异 const compareArrays = (arr1, arr2) => { const diffArr = []; arr2.forEach(item2 => { const item1 = arr1.find(item => item.name === item2.name); if (!item1) { // obj2新增的元素直接加入差异结果 diffArr.push(item2); } else { // 递归比较两个对象的差异 const itemDiff = findDifferences(item1, item2); if (!_.isEmpty(itemDiff)) { // 保留标识键name,合并差异属性 diffArr.push({ ..._.pick(item2, 'name'), ...itemDiff }); } } }); return diffArr.length > 0 ? diffArr : undefined; }; const keys = _.intersection(Object.keys(obj1), Object.keys(obj2)); return keys.reduce((diff, key) => { const val1 = obj1[key]; const val2 = obj2[key]; if (_.isArray(val1) && _.isArray(val2)) { const arrDiff = compareArrays(val1, val2); if (arrDiff) diff[key] = arrDiff; } else if (_.isObject(val1) && _.isObject(val2)) { const nestedDiff = findDifferences(val1, val2); if (!_.isEmpty(nestedDiff)) diff[key] = nestedDiff; } else if (!_.isEqual(val1, val2)) { diff[key] = val2; } return diff; }, {}); };
关键修改点
- 新增
compareArrays函数:通过name字段匹配数组中的对应对象,而非直接比较整个元素是否相等 - 递归比较匹配到的对象,仅保留有差异的属性,同时保留
name标识键以保证结构符合预期 - 仅当数组元素存在差异时,才将其加入结果数组
测试结果
调用findDifferences(obj1, obj2)将返回期望的结果:
{ sofa: [ { name: 'leather', typeSofa: [ { name: 'type1', text: 'type1', quantity: 2 } ] } ] }
扩展说明
如果数组元素没有name这类统一标识键,可根据实际情况修改匹配逻辑(比如使用其他唯一字段,纯值数组则保留原逻辑)。该函数支持多层嵌套的数组和对象差异检测,同时保留最小化的差异结构。
内容的提问来源于stack exchange,提问作者Duy Cao Thiện
相关产品推荐
相关产品推荐

