我有2个不同的嵌套对象,我需要知道它们是否在其中一个嵌套属性中有不同。

var a = {};
var b = {};

a.prop1 = 2;
a.prop2 = { prop3: 2 };

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

对象可以更复杂,有更多嵌套的属性。但这是一个很好的例子。我可以选择使用递归函数或lodash的东西…


当前回答

这是一个简单的带有Lodash深度差异检查器的Typescript,它将生成一个新对象,只包含旧对象和新对象之间的差异。

例如,如果我们有:

const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};

结果对象将是:

const result: {b: 3};

它还兼容多层深层对象,对于数组,它可能需要一些调整。

import * as _ from "lodash";

export const objectDeepDiff = (data: object | any, oldData: object | any) => {
  const record: any = {};
  Object.keys(data).forEach((key: string) => {
    // Checks that isn't an object and isn't equal
    if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
      record[key] = data[key];
    }
    // If is an object, and the object isn't equal
    if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
      record[key] = objectDeepDiff(data[key], oldData[key]);
    }
  });
  return record;
};

其他回答

已经有很多答案发布,但对于那些好奇的人来说,避免编写任何代码来计算具有任何类型结构的两个对象之间的差异,实际上有一个库可以做到这一点。Lodash isEqual只返回true或false,它不返回任何关于更改属性的信息。https://www.npmjs.com/package/deep-diff

它返回两个对象之间差异的完整细节

import DeepDiff from 'deep-diff';
let a = {...} //some object
let b = {...} //some object 
var differences = DeepDiff.diff(a, b);

在这篇文章中也有人提出了类似的问题 获取2个JSON对象之间的差异

一个简单而优雅的解决方案是使用_。isEqual,它执行深度比较:

Var a = {}; Var b = {}; A.prop1 = 2; A.prop2 = {prop3: 2}; B.prop1 = 2; B.prop2 = {prop3: 3}; console.log(_。isEqual (a, b));//如果不同则返回false < script src = " https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js " > < /脚本>

然而,这个解决方案并没有显示哪个属性是不同的。

这是一个简单的带有Lodash深度差异检查器的Typescript,它将生成一个新对象,只包含旧对象和新对象之间的差异。

例如,如果我们有:

const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};

结果对象将是:

const result: {b: 3};

它还兼容多层深层对象,对于数组,它可能需要一些调整。

import * as _ from "lodash";

export const objectDeepDiff = (data: object | any, oldData: object | any) => {
  const record: any = {};
  Object.keys(data).forEach((key: string) => {
    // Checks that isn't an object and isn't equal
    if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
      record[key] = data[key];
    }
    // If is an object, and the object isn't equal
    if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
      record[key] = objectDeepDiff(data[key], oldData[key]);
    }
  });
  return record;
};

我们需要在两个json更新之间获取delta,以跟踪数据库更新。也许其他人会觉得这很有用。

https://gist.github.com/jp6rt/7fcb6907e159d7851c8d59840b669e3d

const {
  isObject,
  isEqual,
  transform,
  has,
  merge,
} = require('lodash');
const assert = require('assert');

/**
 * Perform a symmetric comparison on JSON object.
 * @param {*} baseObj - The base object to be used for comparison against the withObj.
 * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
 * @param {*} invert  - Because this is a symmetric comparison. Some values in the with object
 *                      that doesn't exist on the base will be lost in translation.
 *                      You can execute again the function again with the parameters interchanged.
 *                      However you will lose the reference if the value is from the base or with
 *                      object if you intended to do an assymetric comparison.
 *                      Setting this to true will do make sure the reference is not lost.
 * @returns           - The returned object will label the result of the comparison with the
 *                      value from base and with object.
 */
const diffSym = (baseObj, withObj, invert = false) => transform(baseObj, (result, value, key) => {
  if (isEqual(value, withObj[key])
    && has(withObj, key)) {
    return;
  }

  if (isObject(value)
    && isObject(withObj[key])
    && !Array.isArray(value)) {
    result[key] = diffSym(value, withObj[key], invert);
    return;
  }

  if (!invert) {
    result[key] = {
      base: value,
      with: withObj[key],
    };
    return;
  }

  if (invert) {
    result[key] = {
      base: withObj[key],
      with: value,
    };
  }
});

/**
 * Perform a assymmetric comparison on JSON object.
 * @param {*} baseObj - The base object to be used for comparison against the withObj.
 * @param {*} withObj - The withObject parameter is used as the comparison on the base object.
 * @returns           - The returned object will label the values with
 *                      reference to the base and with object.
 */
const diffJSON = (baseObj, withObj) => {
  // Deep clone the objects so we don't update the reference objects.
  const baseObjClone = JSON.parse(JSON.stringify(baseObj));
  const withObjClone = JSON.parse(JSON.stringify(withObj));

  const beforeDelta = diffSym(baseObjClone, withObjClone);
  const afterDelta = diffSym(withObjClone, baseObjClone, true);

  return merge(afterDelta, beforeDelta);
};

// By Example:

const beforeDataObj = {
  a: 1,
  c: { d: 2, f: 3 },
  g: 4,
  h: 5,
};
const afterDataObj = {
  a: 2,
  b: 3,
  c: { d: 1, e: 1 },
  h: 5,
};

const delta = diffJSON(beforeDataObj, afterDataObj);

// Assert expected result.
assert(isEqual(delta, {
  a: { base: 1, with: 2 },
  b: { base: undefined, with: 3 },
  c: {
    d: { base: 2, with: 1 },
    e: { base: undefined, with: 1 },
    f: { base: 3, with: undefined },
  },
  g: { base: 4, with: undefined },
}));

这是基于@JLavoie,使用lodash

let differences = function (newObj, oldObj) {
      return _.reduce(newObj, function (result, value, key) {
        if (!_.isEqual(value, oldObj[key])) {
          if (_.isArray(value)) {
            result[key] = []
            _.forEach(value, function (innerObjFrom1, index) {
              if (_.isNil(oldObj[key][index])) {
                result[key].push(innerObjFrom1)
              } else {
                let changes = differences(innerObjFrom1, oldObj[key][index])
                if (!_.isEmpty(changes)) {
                  result[key].push(changes)
                }
              }
            })
          } else if (_.isObject(value)) {
            result[key] = differences(value, oldObj[key])
          } else {
            result[key] = value
          }
        }
        return result
      }, {})
    }

https://jsfiddle.net/EmilianoBarboza/0g0sn3b9/8/