Получение diff двух json-объектов - PullRequest
21 голосов
/ 08 декабря 2011

Сценарий: мне нужна функция, которая сравнивает два 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"}]}; 

Или что-то похожее, вы понимаете.

Кто-нибудь знает решение этой проблемы?Я уже нашел один плагин , но я хотел бы знать, есть ли альтернативы.

Ответы [ 3 ]

29 голосов
/ 08 декабря 2011

Можно использовать рекурсивную функцию, которая выполняет итерации по ключам объекта. Затем используйте Object.is для проверки NaN и null. Затем проверьте, является ли второй объект типом, приведенным к false, например 0, 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/

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

РЕДАКТИРОВАТЬ 15/02/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/

6 голосов
/ 11 марта 2014

Вы можете использовать rus-diff https://github.com/mirek/node-rus-diff, который создает MongoDB-совместимый (переименовывать / отменять / устанавливать) 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' } }
5 голосов
/ 07 августа 2014

Внося свои изменения в версию Габриэля Гарца.Этот работает в строгом режиме и снимает проверку массива - всегда будет ложным.Он также удаляет пустые узлы из diff.

                ///639979/kak-proverit-nalichie-pustogo-obekta-javascript
                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;
                };
...