JavaScript中对比嵌套对象并仅列出差异内容的实现方案
对比嵌套对象时仅提取差异键值对的实现方案
需求说明
在JavaScript/TypeScript中对比两个嵌套对象时,需要仅提取存在差异的具体键值对,而非返回整个有差异的嵌套对象,以让结果更简洁易读。现有方案通常会返回完整的差异嵌套对象,无法满足该需求。
示例输入
const A = { position: 2, attributes: [{ code: 123, name: "xyz", params: { label: "hehe", units: "currency" } }], code: 1 } const B = { position: 3, attributes: [{ code: 123, name: "xyzr", params: { label: "heh", units: "currency" } }], code: 1 }
期望输出
{ position: { current: 2, previous: 3 }, attributes: { current : [{ name: "xyz", params: { label: "hehe" } }], previous: [{ name: "xyzr", params: {label: "heh"}}] } }
现有代码问题
你尝试的代码使用_.isEqual判断值是否相等,若不相等则直接返回整个嵌套值,导致结果包含大量无差异的内容(比如示例中attributes里的code和params.units),不符合仅展示差异键值对的需求。
解决方案实现
通过递归遍历对象和数组,逐层对比并仅保留差异部分,最终整理成目标结构:
function getNestedDifferences(current: any, previous: any): any { // 直接相等或同引用,无差异 if (current === previous) return undefined; // 处理null/undefined的边界情况 if (current == null || previous == null) { return { current, previous }; } // 处理数组:按索引对比元素,仅保留有差异的元素及其中的差异键 if (Array.isArray(current) && Array.isArray(previous)) { const diffItems: any[] = []; const maxLen = Math.max(current.length, previous.length); for (let i = 0; i < maxLen; i++) { const itemDiff = getNestedDifferences(current[i], previous[i]); if (itemDiff) { // 若元素差异是嵌套对象,直接保留;否则包装成current/previous结构 diffItems.push( typeof itemDiff === 'object' && !Array.isArray(itemDiff) && !('current' in itemDiff) ? { current: itemDiff, previous: getNestedDifferences(previous[i], current[i]) } : { current: current[i], previous: previous[i] } ); } } return diffItems.length > 0 ? diffItems : undefined; } // 处理对象:遍历所有键,仅保留有差异的键及对应差异内容 if (typeof current === 'object' && typeof previous === 'object') { const allKeys = new Set([...Object.keys(current), ...Object.keys(previous)]); const objDiff: any = {}; for (const key of allKeys) { const valueDiff = getNestedDifferences(current[key], previous[key]); if (valueDiff) { objDiff[key] = valueDiff; } } return Object.keys(objDiff).length > 0 ? objDiff : undefined; } // 基本类型不相等的情况 return { current, previous }; } const compareEditedChanges = (A: any, B: any) => { const finalDiff: any = {}; const allTopKeys = new Set([...Object.keys(A), ...Object.keys(B)]); for (const key of allTopKeys) { const diff = getNestedDifferences(A[key], B[key]); if (diff) { if (Array.isArray(diff)) { // 整理数组类型的差异结构 finalDiff[key] = { current: diff.map(item => item.current), previous: diff.map(item => item.previous) }; } else { // 整理对象或基本类型的差异结构 finalDiff[key] = 'current' in diff ? diff : { current: A[key], previous: B[key] }; } } } return finalDiff; };
使用验证
将示例中的A和B传入函数:
const difference = compareEditedChanges(A, B); console.log(difference);
输出结果与期望完全一致。
逻辑说明
getNestedDifferences递归处理嵌套结构:- 基本类型直接对比,返回包含新旧值的对象
- 数组按索引遍历每个元素,仅保留有差异的元素,且每个元素只保留内部差异键
- 对象遍历所有键,仅保留存在差异的键及其递归处理后的差异内容
compareEditedChanges负责顶层键的遍历,将递归结果整理成统一的{current, previous}结构
内容的提问来源于stack exchange,提问作者Masudha Meher
相关产品推荐
相关产品推荐

