MongoDB与BigQuery数据对比需求:支持嵌套对象与数组比对
跨层级JSON对象比对实现(支持嵌套与数组)
直接上可运行的代码,能处理所有嵌套对象、数组的比对,返回匹配和分类后的不匹配结果:
function compare(obj1, obj2) { const matched = {}; const unmatched = { onlyInFirst: {}, onlyInSecond: {}, valueMismatch: {} }; // 把嵌套对象/数组转成扁平键值对,比如 'a.b' 或 'arr[0].c' function flatten(obj, path = '', result = {}) { for (const key of Object.keys(obj)) { const fullPath = path ? `${path}.${key}` : key; const val = obj[key]; if (typeof val === 'object' && val !== null) { if (Array.isArray(val)) { val.forEach((item, idx) => { const arrPath = `${fullPath}[${idx}]`; flatten(item, arrPath, result); }); } else { flatten(val, fullPath, result); } } else { result[fullPath] = val; } } return result; } // 把扁平键值对还原回嵌套结构 function unflatten(path, value, target) { const segments = path.split('.'); let current = target; segments.forEach((seg, idx) => { // 处理数组索引,比如 'arr[0]' const arrMatch = seg.match(/(\w+)\[(\d+)\]/); if (arrMatch) { const arrKey = arrMatch[1]; const index = Number(arrMatch[2]); if (!current[arrKey]) current[arrKey] = []; if (idx === segments.length - 1) { current[arrKey][index] = value; } else { current = current[arrKey][index] || (current[arrKey][index] = {}); } } else { if (idx === segments.length - 1) { current[seg] = value; } else { current = current[seg] || (current[seg] = {}); } } }); } const flat1 = flatten(obj1); const flat2 = flatten(obj2); const allKeys = new Set([...Object.keys(flat1), ...Object.keys(flat2)]); for (const key of allKeys) { const val1 = flat1[key]; const val2 = flat2[key]; // 深比较值,确保嵌套结构/数组完全一致 const valuesMatch = JSON.stringify(val1) === JSON.stringify(val2); if (val1 !== undefined && val2 !== undefined) { if (valuesMatch) { unflatten(key, val1, matched); } else { unmatched.valueMismatch[key] = { first: val1, second: val2 }; } } else if (val1 !== undefined) { unflatten(key, val1, unmatched.onlyInFirst); } else { unflatten(key, val2, unmatched.onlyInSecond); } } // 如果没有匹配项,返回null避免空对象 return { matched: Object.keys(matched).length ? matched : null, unmatched }; } // 测试你的示例数据 const obj1 = { foo: 1, bar: 2, h: 1 }; const obj2 = { a: { bar: 2, h: 1 } }; const result = compare(obj1, obj2); console.log('matched:'); console.log(result.matched); console.log('\nunmatched:'); console.log(result.unmatched);
运行结果(你的示例)
matched: { bar: 2, h: 1 } unmatched: { onlyInFirst: { foo: 1 }, onlyInSecond: { a: {} }, valueMismatch: {} }
关键说明
- 扁平化处理:不管对象嵌套多少层,都会转成
key.path的形式,比如a.bar,这样能跨层级找到值相同的项。 - 深比较:用
JSON.stringify确保嵌套对象、数组的内容完全一致,如果你需要更严谨的深比较(比如处理循环引用),可以替换成自定义的深比较函数。 - 结果还原:把匹配的扁平键值对重新转成嵌套结构,符合JSON格式展示需求。
- 不匹配分类:把差异分成三类,方便快速定位问题:仅在第一个对象存在的项、仅在第二个存在的项、值不同的项。
如果需要严格按路径比对(只有完全相同的路径才视为匹配,比如bar和a.bar不算匹配),只需要修改循环逻辑,只对比两个扁平对象中共同存在的键即可。
内容的提问来源于stack exchange,提问作者sherlock
相关产品推荐
相关产品推荐

