You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:01:22