【问题标题】:Compare Two JSON Objects to See Changes Angular比较两个 JSON 对象以查看 Angular 的变化
【发布时间】:2016-01-27 09:43:47
【问题描述】:

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

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

【问题讨论】:

标签: javascript json angularjs


【解决方案1】:

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

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

正如 cmets 中所确定的,这将需要使用引用和存在检查的混合来递归遍历对象。

以下算法是一个想法的快速实现。遍历对象并使用对象列表描述它们的更改。就像对象本身一样,更改是嵌套的,但根据它们在对象中的位置(因此可以展平)具有唯一的 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,而不是从头到尾比较参考。

【讨论】:

  • 做了很多 angular.equals() 已经内置的工作
  • 谢谢,丹。这绝对可以帮助我了解如何实施。
  • 这不适用于对象数组和嵌套对象,您也有解决方案吗?
【解决方案2】:

我将在此处添加我对 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, '');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 2020-03-27
    • 2023-03-26
    相关资源
    最近更新 更多