比较两个JSON对象以查看更改角度

时间:2015-10-27 19:46:08

标签: javascript json angularjs

我需要重构我的程序以获取JSON obj,将其存储在某处,进行一系列更改,然后比较这两个对象以查看已更改,删除和添加的内容。

我不确定如何在JS中执行此操作,那么有人可以建议在Angular中执行此操作(对象比较部分)吗?否则,我将不得不对我的程序运行/从后端尝试它的方式进行大量更改。感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

对象比较的唯一内置操作是== / ===等于运算符,它们使用引用相等:A是B,而不是A等于B.

您想要的是描述两个对象之间差异的更改描述符列表。

正如评论中所指出的,这需要使用参考和存在检查的混合物对对象进行递归遍历。

以下算法是一个想法的快速实现。遍历对象,并使用对象列表描述其更改。就像对象本身一样,更改是嵌套的,但是根据它们在对象中的位置具有唯一的id(因此它可以被展平)。

function diff(a, b, namespace) {
  namespace = (namespace || '') + '.';

  var keysInA = Object.keys(a),
      keysInB = Object.keys(b);

  var diffA = keysInA.reduce(function(changes, key) {
    var ns = namespace + key;

    if(typeof b[key] == 'undefined') {
      return changes.concat([{ type: 'DELETED', id: ns }]);
    }

    if(a[key] !== b[key]) {
      return changes.concat([{ type: 'CHANGED', id: ns }]);
    }

    if(typeof a[key] == b[key] == 'object') {
      return diff(a[key], b[key], ns);
    }

    return changes; 
  }, []);

  var diffB = keysInB.reduce(function(changes, key) {
    var ns = namespace + key;

    if(typeof a[key] == 'undefined') {
      return changes.concat([{ type: 'ADDED', id: ns }]);
    }

    return changes;
  }, []);

  return diffA.concat(diffB);
}

例如,我们采用对象的原始状态。

var a = { a: 1, b: 2, c: 3 };

新州。

var b = { a: 2, c: 3, d: 5 };

然后使用diff函数运行它们。

diff(a, b);

返回更改列表。

[
  { id: '.a', type: 'CHANGED' },
  { id: '.b', type: 'DELETED' },
  { id: '.d', type: 'ADDED' }
]

显然,您必须调整此算法,使其符合您构成变更的标准。您可能希望查看deep equality,而不是整体比较参考文献。

答案 1 :(得分:0)

我将在这里添加我对Dan的建议的实现,以防它有助于想要看到实际实现的人:

var propertiesToIgnore = ['.indexesTracked', '.notInSyncWithDb', '.date', '.details.updatedAt', '.autoLoadLocalStorage', '.deletionQueue']; //these are extraneous properties added to project that should not be represented in interface (and not tracked)
var keysToIgnore = ['addAllDatacuts', 'datacutNames']; // this just looks at the property rather than the above which matches from the project root

function diff(a, b, namespace, firstCall) {
    namespace = firstCall ? (namespace || '') : (namespace || '') + '.';

    var keysInA = Object.keys(a),
        keysInB = Object.keys(b);

    var diffA = keysInA.reduce(function(changes, key) {
      var ns = namespace + key;

      if (propertiesToIgnore.indexOf(ns) !== -1 || keysToIgnore.indexOf(key) !== -1) {
        return changes;
      }

      if (key == '$$hashKey') {
        return changes;
      }

      if (angular.equals(a[key], b[key])) { // whole chain is equal so I do not need to iterate over this branch
        return changes;
      }

      if (typeof b[key] == 'undefined') {
        return changes.concat([{ type: 'DELETED', id: ns }]);
      }

      if (a[key] !== b[key] && (typeof b[key] !== 'object' || typeof a[key] !== 'object')) {
        return changes.concat([{ type: 'UPDATED', id: ns }]);
      }

      if (typeof b[key] === 'object' && typeof a[key] === 'object') {
        return changes.concat(diff(a[key], b[key], ns));
      }
      if (a[key] === null || b[key] === null) { // avoids values that are null as js considers null an object
        return changes;
      }

      if(typeof a[key] == 'object' && typeof b[key] == 'object' && typeof a[key].getMonth !== 'function' && typeof b[key].getMonth !== 'function') { // last part necessary to make sure it is not a date object
        return diff(a[key], b[key], ns);
      }

      return changes;
    }, []);

    var diffB = keysInB.reduce(function(changes, key) {
      var ns = namespace + key;

      if (propertiesToIgnore.indexOf(ns) !== -1 || keysToIgnore.indexOf(key) !== -1) {
        return changes;
      }

      if (key == '$$hashKey') {
        return changes;
      }

      if (typeof a[key] == 'undefined') {
        return changes.concat([{ type: 'ADDED', id: ns }]);
      }

      return changes;
    }, []);

    return diffA.concat(diffB);
  }

  $scope.changes = diff(dbData, $scope.project, '');
相关问题