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

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

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

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

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


当前回答

我知道这并不能直接回答OP的问题,但我是通过搜索如何删除lodash被引导到这里的。希望这能帮助到和我处境相似的人。

这要归功于@JohanPersson。我在这个答案的基础上实现了对深度嵌套值的比较,并获得对差异的键引用

getObjectDiff = (obj1, obj2) => { const obj1Props = Object.keys(obj1); const obj2Props = Object.keys(obj2); const keysWithDiffValue = obj1Props.reduce((keysWithDiffValueAccumulator, key) => { const propExistsOnObj2 = obj2.hasOwnProperty(key); const hasNestedValue = obj1[key] instanceof Object && obj2[key] instanceof Object; const keyValuePairBetweenBothObjectsIsEqual = obj1[key] === obj2[key]; if (!propExistsOnObj2) { keysWithDiffValueAccumulator.push(key); } else if (hasNestedValue) { const keyIndex = keysWithDiffValueAccumulator.indexOf(key); if (keyIndex >= 0) { keysWithDiffValueAccumulator.splice(keyIndex, 1); } const nestedDiffs = getObjectDiff(obj1[key], obj2[key]); for (let diff of nestedDiffs) { keysWithDiffValueAccumulator.push(`${key}.${diff}`); } } else if (keyValuePairBetweenBothObjectsIsEqual) { const equalValueKeyIndex = keysWithDiffValueAccumulator.indexOf(key); keysWithDiffValueAccumulator.splice(equalValueKeyIndex, 1); } return keysWithDiffValueAccumulator; }, obj2Props); return keysWithDiffValue; } const obj1 = {a0: {a1: {a2: {a3: 'Im here'}}}}; const obj2 = {a0: {a1: {a2: {a3: 'Not here', b3: 'some'}}}}; console.log('final', getObjectDiff(obj1, obj2));

其他回答

对于无意中发现这条线索的人,这里有一个更完整的解决方案。它将比较两个对象,并给出所有属性的键,这些属性要么只存在于object1中,要么只存在于object2中,要么同时存在于object1和object2中,但值不同:

/*
 * Compare two objects by reducing an array of keys in obj1, having the
 * keys in obj2 as the intial value of the result. Key points:
 *
 * - All keys of obj2 are initially in the result.
 *
 * - If the loop finds a key (from obj1, remember) not in obj2, it adds
 *   it to the result.
 *
 * - If the loop finds a key that are both in obj1 and obj2, it compares
 *   the value. If it's the same value, the key is removed from the result.
 */
function getObjectDiff(obj1, obj2) {
    const diff = Object.keys(obj1).reduce((result, key) => {
        if (!obj2.hasOwnProperty(key)) {
            result.push(key);
        } else if (_.isEqual(obj1[key], obj2[key])) {
            const resultKeyIndex = result.indexOf(key);
            result.splice(resultKeyIndex, 1);
        }
        return result;
    }, Object.keys(obj2));

    return diff;
}

下面是一个输出示例:

// Test
let obj1 = {
    a: 1,
    b: 2,
    c: { foo: 1, bar: 2},
    d: { baz: 1, bat: 2 }
}

let obj2 = {
    b: 2, 
    c: { foo: 1, bar: 'monkey'}, 
    d: { baz: 1, bat: 2 }
    e: 1
}
getObjectDiff(obj1, obj2)
// ["c", "e", "a"]

如果你不关心嵌套对象并且想要跳过lodash,你可以替换_。isEqual用于正常的值比较,例如obj1[key] === obj2[key]。

如果你只需要键比较:

 _.reduce(a, function(result, value, key) {
     return b[key] === undefined ? key : []
  }, []);

以Sridhar Gudimela的回答为基础,下面以一种使用TypeScript的方式进行了更新:

///  U T I L S

interface LooseObjectInterface {
  [key: string]: any;
};

type inputOptions = LooseObjectInterface | any[];



///  E X P O R T

export const objectCompare = (objectA: inputOptions, objectB: inputOptions): LooseObjectInterface => {
  let diffObj: LooseObjectInterface = {};

  switch(true) {
    case (Array.isArray(objectA)):
      objectA.forEach((elem: any, index: number) => {
        if (!Array.isArray(diffObj))
          diffObj = [];

        diffObj[index] = objectCompare(elem, (objectB || [])[index]);
      });

      break;

    case (objectA !== null && typeof objectA === "object"):
      Object.keys(objectA).forEach((key: any) => {
        if (Array.isArray(objectA[key])) {
          let arr = objectCompare(objectA[key], objectB[key]);

          if (!Array.isArray(arr))
            arr = [];

          arr.forEach((elem: any, index: number) => {
            if (!Array.isArray(diffObj[key]))
              diffObj[key] = [];

            diffObj[key][index] = elem;
          });
        } else if (typeof objectA[key] === "object")
          diffObj[key] = objectCompare(objectA[key], objectB[key]);
        else if (objectA[key] !== (objectB || {})[key])
          diffObj[key] = objectA[key];
        else if (objectA[key] === (objectB || {})[key])
          delete objectA[key];
      });

      break;

    default:
      break;
  }

  Object.keys(diffObj).forEach((key: any) => {
    if (typeof diffObj[key] === "object" && JSON.stringify(diffObj[key]) === "{}")
      delete diffObj[key];
  });

  return diffObj;
};

编辑:我最初的回答使用了Flow,因此被否决了(我猜,或者可能是因为我的回答没有使用Lodash……)然而,有一个类似问题的答案也无妨)。

此解决方案返回一个具有修改过的属性的对象。

_.reduce(a, (r, v, k) => { return _.merge(r, _.isEqual(v, b[k]) ? {} : { [k]: v }); }, {});

这是我对这个问题的解决办法

const _ = require('lodash');

var objects = [{ 'x': 1, 'y': 2, 'z':3, a:{b:1, c:2, d:{n:0}}, p:[1, 2, 3]  }, { 'x': 2, 'y': 1, z:3, a:{b:2, c:2,d:{n:1}}, p:[1,3], m:3  }];

const diffFn=(a,b, path='')=>_.reduce(a, function(result, value, key) {

    if(_.isObjectLike(value)){
      if(_.isEqual(value, b[key])){
        return result;
      }else{

return result.concat(diffFn(value, b[key], path?(`${path}.${key}`):key))
      }
    }else{
return _.isEqual(value, b[key]) ?
        result : result.concat(path?(`${path}.${key}`):key);
    }
    
}, []);

const diffKeys1=diffFn(objects[0], objects[1])
const diffKeys2=diffFn(objects[1], objects[0])
const diffKeys=_.union(diffKeys1, diffKeys2)
const res={};

_.forEach(diffKeys, (key)=>_.assign(res, {[key]:{ old: _.get(objects[0], key), new:_.get(objects[1], key)} }))

res
/*
Returns
{
  x: { old: 1, new: 2 },
  y: { old: 2, new: 1 },
  'a.b': { old: 1, new: 2 },
  'a.d.n': { old: 0, new: 1 },
  'p.1': { old: 2, new: 3 },
  'p.2': { old: 3, new: undefined },
  m: { old: undefined, new: 3 }
}
*/