Сравните два объекта JSON, чтобы увидеть изменения под углом

0

Мне нужно реорганизовать мою программу, чтобы взять JSON obj, сохранить его где-нибудь, сделать кучу изменений, а затем сравнить два объекта, чтобы увидеть, что было изменено, удалено и добавлено.

Я не уверен в том, как это сделать в JS, так может ли кто-нибудь посоветовать сделать это в Angular (часть сравнения объектов)? В противном случае мне придется внести массу изменений в способ запуска/запуска моей программы из бэкэнд. Цените любую помощь.

  • 0
    Мне не нужно равенство. Мне нужна разница между предметами
Показать ещё 4 комментария

2 ответа

2
Лучший ответ

Единственной встроенной операцией для сравнения объектов являются операторы равенства ==/===, которые используют ссылочное равенство: A есть B, а не A равно B.

Вам нужен список описателей изменений, описывающих разницу между двумя объектами.

Как указано в комментариях, это потребует рекурсивного обхода объектов, используя смесь контрольных и бытовых проверок.

Следующий алгоритм - это быстрая реализация идеи. Объекты перемещаются, и их изменения описываются списком объектов. Как и сами объекты, изменения вложены, но имеют уникальный идентификатор, основанный на их местоположении внутри объекта (чтобы его можно было сгладить).

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' }
]

Очевидно, вам придется адаптировать этот алгоритм, чтобы он соответствовал вашим критериям для того, что представляет собой изменение. Возможно, вы захотите взглянуть на глубокое равенство, а не на сравнение ссылок.

  • 1
    делает большую работу, angular.equals() уже встроена в angular.equals()
  • 0
    Спасибо, Дэн. Это определенно помогает мне понять, как это можно реализовать.
Показать ещё 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, '');

Ещё вопросы

Сообщество Overcoder
Наверх
Меню