比较两个JSON对象以查看角度变化

Compare Two JSON Objects to See Changes Angular

本文关键字:变化 对象 JSON 两个 比较      更新时间:2023-09-26

我需要重构我的程序,以获取一个JSON对象,将其存储在某个地方,进行一系列更改,然后比较这两个对象,查看更改、删除和添加了什么。

我不确定在JS中有什么方法可以做到这一点,所以有人能在Angular(对象比较部分)中建议一种方法吗?否则,我将不得不对我的程序运行方式进行大量更改/从后端尝试。感谢您的帮助。

对象比较的唯一内置操作是==/===相等运算符,它使用引用相等: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' }
]

显然,您必须调整此算法,使其符合您的变更标准。你可能想看看深层次的平等,而不是一直比较引用。

我将在这里添加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, '');