web-dev-qa-db-ja.com

2つのJSONオブジェクトのdiffを取得する

シナリオ:2つのJSONオブジェクトを比較し、違いのリストと、可能であればカバレッジメトリックなどのデータを含むJSONオブジェクトを返す関数が必要です。

_var madrid = '{"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]}';
var barca = '{"type":"team","description":"Bad","trophies":[{"ucl":"3"}]}';
_

compare(madrid, barca)を実行した場合、返されるオブジェクトは次のようになります。

_{"description" : "Bad", "trophies":[{"ucl":"3"}, {"copa":"5"}]}; 
_

または似たようなもの、あなたはアイデアを得る。

誰もがこれに対する解決策を知っていますか? plugin をすでに見つけていますが、他に選択肢があるかどうか知りたいです。

23
Soroush Hakami

オブジェクトキーで反復する再帰関数を使用することが可能です。次に、 Object.is を使用して、NaNおよびnullをテストします。次に、2番目のオブジェクトが、0false、またはNaNのようなnullにキャストするタイプかどうかをテストします。両方のオブジェクトのキーをリストし、それらを連結してobj1の欠落キーをテストし、それを繰り返します。

同じキー値に違いがある場合、object2の値を保存して続行します。両方のキー値がオブジェクトである場合、再帰的に比較できることを意味します。

function diff(obj1, obj2) {
    const result = {};
    if (Object.is(obj1, obj2)) {
        return undefined;
    }
    if (!obj2 || typeof obj2 !== 'object') {
        return obj2;
    }
    Object.keys(obj1 || {}).concat(Object.keys(obj2 || {})).forEach(key => {
        if(obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {
            result[key] = obj2[key];
        }
        if(typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {
            const value = diff(obj1[key], obj2[key]);
            if (value !== undefined) {
                result[key] = value;
            }
        }
    });
    return result;
}

上記のコードはBSDライセンスであり、どこでも使用できます。

テストリンク: https://jsfiddle.net/gartz/vy9zaof2/54/

重要な観察、これは配列をオブジェクトに変換し、同じインデックス位置の値を比較します。複雑さが必要なため、この関数でカバーされない配列を比較する方法は他にもたくさんあります。

EDIT 2/15/2019:この回答は、新しいES2017構文を追加し、コメントからのユースケースを修正するために変更されました。


これは単なるキックオフであり、まだテストしていませんが、フィルターまたはコンパレーター機能から始めました。これは再帰的であり、優先結果を取得する必要がある場合は変更します。

function filter(obj1, obj2) {
    var result = {};
    for(key in obj1) {
        if(obj2[key] != obj1[key]) result[key] = obj2[key];
        if(typeof obj2[key] == 'array' && typeof obj1[key] == 'array') 
            result[key] = arguments.callee(obj1[key], obj2[key]);
        if(typeof obj2[key] == 'object' && typeof obj1[key] == 'object') 
            result[key] = arguments.callee(obj1[key], obj2[key]);
    }
    return result;
}

テスト: http://jsfiddle.net/gartz/Q3BtG/2/

30
Gabriel Gartz

Rus-diff https://github.com/mirek/node-rus-diff を使用して、MongoDB互換(rename/unset/set)diffを作成できます。

// npm install rus-diff
var madrid = {"type":"team","description":"Good","trophies":[{"ucl":"10"}, {"copa":"5"}]};
var barca = {"type":"team","description":"Bad","trophies":[{"ucl":"3"}]};
var rusDiff = require('rus-diff').rusDiff
console.log(rusDiff(madrid, barca))

出力:

{ '$unset': { 'trophies.1': true },
  '$set': { description: 'Bad', 'trophies.0.ucl': '3' } }
6
Mirek Rusin

gabriel Gartzのバージョンへの私の変更の貢献。これは厳密モードで動作し、配列チェックを削除します-常にfalseになります。また、空のノードを差分から削除します。

                //http://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object
                var isEmptyObject = function(obj) {
                    var name;
                    for (name in obj) {
                        return false;
                    }
                    return true;
                };

                //http://stackoverflow.com/questions/8431651/getting-a-diff-of-two-json-objects
                var diff = function(obj1, obj2) {
                    var result = {};
                    var change;
                    for (var key in obj1) {
                        if (typeof obj2[key] == 'object' && typeof obj1[key] == 'object') {
                            change = diff(obj1[key], obj2[key]);
                            if (isEmptyObject(change) === false) {
                                result[key] = change;
                            }
                        }
                        else if (obj2[key] != obj1[key]) {
                            result[key] = obj2[key];
                        }
                    }
                    return result;
                };
5
Leblanc Meneses