У меня есть два объекта: oldObj
и newObj
.
Данные в oldObj
были использованы для заполнения формы и newObj
являются результатом того, что пользователь изменил данные в этой форме и отправил ее.
Оба объекта глубокие, т.е. у них есть свойства, которые являются объектами или массивами объектов и т. д. - они могут иметь глубину n уровней, поэтому алгоритм diff должен быть рекурсивным.
Теперь мне нужно не только выяснить, что было изменено (как добавлено / обновлено / удалено) из oldObj
to 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]
, что достаточно просто для массивов типов, основанных на значениях, таких как string, int и bool, но становится действительно трудным в обращении, когда дело доходит до массивы ссылочных типов, таких как объекты и массивы.
Пример массива, который должен быть найден равным:
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
Не только довольно сложно проверить этот тип глубокого равенства, но и найти хороший способ представить возможные изменения.
источник
newObj
генерируется js-кодом для чтения значений из формы в DOM. Есть несколько способов сохранить состояние и сделать это намного проще, но я бы хотел оставить его без сохранения состояния в качестве упражнения. Также я ищу предшествующий уровень техники, чтобы увидеть, как другие могли бы справиться с этим, если действительно кто-то есть.Ответы:
Я написал небольшой класс, который делает то, что вы хотите, вы можете проверить это здесь .
Единственное, что отличается от вашего предложения, это то, что я не считаю
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
его одним и тем же, потому что считаю, что массивы не равны, если порядок их элементов не одинаков. Конечно, это можно изменить при необходимости. Также этот код может быть дополнительно расширен, чтобы принимать функцию в качестве аргумента, который будет использоваться для произвольного форматирования объекта diff на основе переданных значений примитивов (теперь эта работа выполняется методом «CompareValues»).источник
c
создается как,undefined
но должна быть строкой'i am created'
), и, кроме того, он не делает то, что мне нужно, так как ему не хватает сравнения значений глубокого массива, которое является самая важная (и сложная / трудная) часть. Как примечание стороны, конструкция'array' != typeof(obj)
бесполезна, так как массивы - это объекты, которые являются экземплярами массивов.{type: ..., data:..}
объекта. Чего не хватает, так это поиска значения из первого массива за секунду, но, как я уже упоминал в своем ответе, я не думаю, что массивы равны, если порядок их значений не одинаков ([1, 2, 3] is not equal to [3, 2, 1]
на мой взгляд).[{key: 'value1'}] and [{key: 'value2'}, {key: 'value3'}]
. Теперь первый объект в первом массиве обновлен значениями «value1» или «value2». И это простой пример, он может быть намного сложнее с глубоким вложением. Если вы хотите / нужно глубоко вложенность сравнение независимо от положения ключа не создавать массивы объектов, создавать объекты с вложенными объектами , как для предыдущего примера:{inner: {key: 'value1'}} and {inner: {key: 'value2'}, otherInner: {key: 'value3'}}
.Используя Underscore, простой diff:
Результаты в частях,
o1
которые соответствуют, но с другими значениями вo2
:Это было бы иначе для глубокого различия:
Как отмечает @Juhana в комментариях, приведенное выше является только diff a -> b и необратимым (то есть дополнительные свойства в b будут игнорироваться). Используйте вместо a -> b -> a:
См. Http://jsfiddle.net/drzaus/9g5qoxwj/ для полного примера + тесты + миксины
источник
omit
что это будет глубокий провал, но был неправ, поэтому включил и для сравнения.r[k] = ... : v
вr[k] = ... : {'a':v, 'b':b[k] }
, таким образом , вы можете увидеть два значения.{a:1, b:2}
и{a:1, b:2, c:3}
._.omitBy
вместо_.omit
.Я хотел бы предложить решение ES6 ... Это односторонняя разность, то есть она будет возвращать ключи / значения
o2
, которые не идентичны их аналогам вo1
:источник
if(o1[key] === o1[key])
строку, чувакUncaught SyntaxError: Unexpected token ...
Используя Lodash:
Я не использую ключ / объект / источник, но я оставил его там, если вам нужен доступ к ним. Сравнение объектов просто не позволяет консоли печатать различия в консоли от самого внешнего элемента до самого внутреннего элемента.
Вы можете добавить немного логики для обработки массивов. Возможно, сначала отсортируйте массивы. Это очень гибкое решение.
РЕДАКТИРОВАТЬ
Изменено с _.merge на _.mergeWith из-за обновления lodash. Спасибо Aviron за то, что заметил изменение.
источник
Вот библиотека JavaScript, которую вы можете использовать для поиска различий между двумя объектами JavaScript:
URL Github: https://github.com/cosmicanant/recursive-diff
URL-адрес Npmjs: https://www.npmjs.com/package/recursive-diff
Вы можете использовать библиотеку recursive-diff в браузере, а также Node.js. Для браузера сделайте следующее:
Принимая во внимание, что в node.js вам может потребоваться модуль 'recursive-diff' и использовать его, как показано ниже:
источник
В наши дни для этого доступно немало модулей. Я недавно написал модуль для этого, потому что я не был удовлетворен многочисленными модулями сравнения, которые я нашел. Его называют
odiff
: https://github.com/Tixit/odiff . Я также перечислил несколько самых популярных модулей и почему они не былиodiff
приняты в файле readme , которые вы могли бы просмотреть, если уodiff
вас нет нужных свойств. Вот пример:источник
Существует модуль npm с более чем 500 тыс. Загрузок в неделю: https://www.npmjs.com/package/deep-object-diff
Мне нравится объект как представление различий - особенно легко увидеть структуру, когда она сформирована.
источник
Я использовал этот кусок кода для выполнения задачи, которую вы описываете:
это даст вам новый объект, который объединит все изменения между старым объектом и новым объектом из вашей формы
источник
$.extend(true,obj1,obj2)
с помощью jQuery. Это совсем не то, что мне нужно. Мне нужна разница между двумя объектами, а не их комбинация.Я разработал функцию с именем «CompareValue ()» в Javascript. он возвращает, является ли значение тем же или нет. Я вызвал CompareValue () для цикла одного объекта. Вы можете получить разницу двух объектов в diffParams.
источник
Я знаю, что опаздываю на вечеринку, но мне нужно было что-то похожее, чтобы приведенные выше ответы не помогли.
Я использовал функцию $ watch Angular для обнаружения изменений в переменной. Мне нужно было не только узнать, изменилось ли свойство переменной, но я также хотел убедиться, что измененное свойство не было временным вычисляемым полем. Другими словами, я хотел игнорировать определенные свойства.
Вот код: https://jsfiddle.net/rv01x6jo/
Вот как это использовать:
Надеюсь, это кому-нибудь поможет.
источник
Я просто использую ramda, для решения той же проблемы, мне нужно знать, что изменилось в новом объекте. Так что вот мой дизайн.
результат, имя свойства и его статус.
источник
Вот машинописная версия кода @sbgoran
источник
Вот модифицированная версия чего-то найденного на gisthub .
источник
Я изменил ответ @ sbgoran так, чтобы результирующий объект diff включал только измененные значения и пропускал значения, которые были одинаковыми. Кроме того, он показывает как исходное значение, так и обновленное значение .
источник
Я уже написал функцию для одного из моих проектов, которая сравнивает объект как параметры пользователя с его внутренним клоном. Он также может проверять и даже заменять значения по умолчанию, если пользователь ввел неверный тип данных или удалил его в чистом javascript.
В IE8 100% работает. Проверено успешно.
/ * результат
источник
Более расширенная и упрощенная функция из ответа sbgoran.
Это позволяет глубокое сканирование и найти сходство массива.
источник
Я наткнулся здесь, пытаясь найти способ получить разницу между двумя объектами. Это мое решение с использованием Lodash:
источник
Я взял ответ выше @sbgoran и изменил его для своего случая так же, как и вопрос, необходимый для обработки массивов как наборов (т.е. порядок не важен для diff)
источник
Вот решение, которое:
object
типа)undefined
Сначала мы определяем интерфейс результата сравнения:
с особым случаем изменения, когда мы хотим знать, что такое старые и новые значения:
Тогда мы можем предоставить
diff
функцию, которая является просто двумя циклами (с рекурсивностью, еслиdeep
естьtrue
):В качестве примера звоним:
вернется:
и вызов того же с
deep
третьим параметром вернет:источник
Эта библиотека одна из самых быстрых для создания глубоких различий объектов:
https://www.npmjs.com/package/@netilon/differify
источник