2つのオブジェクト間の一般的なディープ差分 質問する

2つのオブジェクト間の一般的なディープ差分 質問する

oldObjとという 2 つのオブジェクトがありますnewObj

のデータはoldObjフォームに入力するために使用され、newObjユーザーがこのフォームでデータを変更して送信した結果です。

どちらのオブジェクトも深く、つまり、オブジェクトまたはオブジェクトの配列などのプロパティを持ちます。n レベルの深さになる可能性があるため、diff アルゴリズムは再帰的である必要があります。

oldObjここで、からに何が変更されたか (追加/更新/削除) を把握するだけでなくnewObj、それを最も適切に表現する方法も把握する必要があります。

これまでのところ、genericDeepDiffBetweenObjectsフォームにオブジェクトを返すメソッドを構築するだけを考えていました{add:{...},upd:{...},del:{...}}が、以前誰かがこれを必要としていたに違いないと考えました。

それで...これを実行し、さらに違いを表現するより良い方法(JSON シリアル化可能な方法で)を持つライブラリまたはコードを誰か知っていますか?

アップデート:

newObjと同じオブジェクト構造を使用しながら、すべてのプロパティ値をフォーム上のオブジェクトに変換することで、更新されたデータを表すより良い方法を思いつきました。

{type: '<update|create|delete>', data: <propertyValue>}

だからもしそれnewObj.prop1 = 'new value'oldObj.prop1 = 'old value'設定されればreturnObj.prop1 = {type: 'update', data: 'new value'}

アップデート2:

配列であるプロパティになると、配列は[1,2,3]と等しいとみなされる必要があるため[2,3,1]、本当に厄介になります。これは、文字列、int、bool などの値ベースの型の配列の場合は簡単ですが、オブジェクトや配列などの参照型の配列になると、処理が非常に難しくなります。

等しいと判断される配列の例:

[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]

この種の深い値の等価性をチェックするのは非常に複雑であるだけでなく、起こり得る変化を表現する適切な方法を見つけることも複雑です。

ベストアンサー1

必要なことを実行する小さなクラスを作成しました。テストすることができますここ

あなたの提案と違うのは、私が考慮していないことです

[1,[{c: 1},2,3],{a:'hey'}]

そして

[{a:'hey'},1,[3,{c: 1},2]]

同じにする必要があります。なぜなら、配列の要素の順序が同じでない場合、配列は等しくないと考えるからです。もちろん、必要に応じて変更できます。また、このコードは、渡されたプリミティブ値に基づいて任意の方法で diff オブジェクトをフォーマットするために使用される関数を引数として受け取るようにさらに拡張できます (現在、この作業は "compareValues" メソッドによって実行されます)。

var deepDiffMapper = function () {
  return {
    VALUE_CREATED: 'created',
    VALUE_UPDATED: 'updated',
    VALUE_DELETED: 'deleted',
    VALUE_UNCHANGED: 'unchanged',
    map: function(obj1, obj2) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
        throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
        return {
          type: this.compareValues(obj1, obj2),
          data: obj1 === undefined ? obj2 : obj1
        };
      }

      var diff = {};
      for (var key in obj1) {
        if (this.isFunction(obj1[key])) {
          continue;
        }

        var value2 = undefined;
        if (obj2[key] !== undefined) {
          value2 = obj2[key];
        }

        diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
        if (this.isFunction(obj2[key]) || diff[key] !== undefined) {
          continue;
        }

        diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

    },
    compareValues: function (value1, value2) {
      if (value1 === value2) {
        return this.VALUE_UNCHANGED;
      }
      if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return this.VALUE_UNCHANGED;
      }
      if (value1 === undefined) {
        return this.VALUE_CREATED;
      }
      if (value2 === undefined) {
        return this.VALUE_DELETED;
      }
      return this.VALUE_UPDATED;
    },
    isFunction: function (x) {
      return Object.prototype.toString.call(x) === '[object Function]';
    },
    isArray: function (x) {
      return Object.prototype.toString.call(x) === '[object Array]';
    },
    isDate: function (x) {
      return Object.prototype.toString.call(x) === '[object Date]';
    },
    isObject: function (x) {
      return Object.prototype.toString.call(x) === '[object Object]';
    },
    isValue: function (x) {
      return !this.isObject(x) && !this.isArray(x);
    }
  }
}();


var result = deepDiffMapper.map({
  a: 'i am unchanged',
  b: 'i am deleted',
  e: {
    a: 1,
    b: false,
    c: null
  },
  f: [1, {
    a: 'same',
    b: [{
      a: 'same'
    }, {
      d: 'delete'
    }]
  }],
  g: new Date('2017.11.25')
}, {
  a: 'i am unchanged',
  c: 'i am created',
  e: {
    a: '1',
    b: '',
    d: 'created'
  },
  f: [{
    a: 'same',
    b: [{
      a: 'same'
    }, {
      c: 'create'
    }]
  }, 1],
  g: new Date('2017.11.25')
});
console.log(result);

おすすめ記事