对象与递归函数的比较

Objects comparison with recursive function

本文关键字:比较 递归函数 对象      更新时间:2023-09-26

考虑以下函数的蓝图,该蓝图试图比较两个对象:

function objectCompare(a,b,path){ 
    for (var prop in a) {
      path=prop;
      if (a.hasOwnProperty(prop) && !(b.hasOwnProperty(prop))) {
                ...
          return false;
      }
      ...
      if (detectType(a[prop])==='Object'){
          if (!objectCompare(a[prop],b[prop],path)) 
               return false;
      }
      ...
    }
      return true;
}

detectType是我自己的函数,它检查变量的类型。我的问题是,每次进行递归调用时,我都希望丰富变量path。然而,在递归调用完成的同时,path必须遍历初始对象的剩余属性名,而不会被丰富。。。想象一下以下对象:

var Obj1 = {
        p1: 's',
        p2: {
          p1: {a: { propA: 'a', propB: 'b' }},
          p2: 'g',
        }
      };
var Obj2 = {
        p1: 's',
        p2: {
          p1: {a: { propA: 'a', propB: 'c' }},
          p2: 'g',
        }
      };

当函数objectCompare返回时,我希望path具有以下值:p2.p1.a.propB,即使两个对象不同的点。我怎样才能做到这一点?

您必须将当前键添加到路径中,并将新路径传递给递归调用。考虑:

console.info=function(x){document.write('<pre>'+JSON.stringify(x,0,3)+'</pre>')}
//--
// compare: return path if a,b are different, null otherwise
function compare(a, b, path) {
    var ta = typeof a,
        tb = typeof b;
    // different types - fail
    
    if (ta !== tb) {
        return path;
    }
    
    // scalars - null if equal, path if not
    if (ta !== 'object') {
        return a === b ? null : path;
    }
    
    // make a set of keys from both objects
    var keys = Object.keys(a).concat(Object.keys(b)).filter(function(x, i, self) {
        return self.indexOf(x) === i;
    });
    // for each key in set, compare values
    
    var res = null;
    keys.some(function(k) {
        return res = compare(a[k], b[k], path.concat(k));
    });
    // return null or the first failed path
    
    return res;
}
//
var Obj1 = {
        p1: 's',
        p2: {
          p1: {a: { propA: 'a', propB: 'b' }},
          p2: 'g',
        }
      };
var Obj2 = {
        p1: 's',
        p2: {
          p1: {a: { propA: 'a', propB: 'c' }},
          p2: 'g',
        }
      };
var res = compare(Obj1, Obj2, [])
console.info(res);

deep-diff可以满足您的需要。

上面的参考文献给出了这个示例代码:

var diff = require('deep-diff').diff;
var lhs = {
    name: 'my object',
    description: 'it''s an object!',
    details: {
        it: 'has',
        an: 'array',
        with: ['a', 'few', 'elements']
    }
};
var rhs = {
    name: 'updated object',
    description: 'it''s an object!',
    details: {
        it: 'has',
        an: 'array',
        with: ['a', 'few', 'more', 'elements', { than: 'before' }]
    }
};
var differences = diff(lhs, rhs);

上面的代码片段将产生以下描述差异的结构:

[ { kind: 'E',
    path: [ 'name' ],
    lhs: 'my object',
    rhs: 'updated object' },
  { kind: 'E',
    path: [ 'details', 'with', 2 ],
        lhs: 'elements',
        rhs: 'more' },
  { kind: 'A',
    path: [ 'details', 'with' ],
    index: 3,
    item: { kind: 'N', rhs: 'elements' } },
  { kind: 'A',
    path: [ 'details', 'with' ],
    index: 4,
    item: { kind: 'N', rhs: { than: 'before' } } } ]

值得注意的是,path属性与您想要的输出非常相似。