Оператор строгого равенства сообщит вам, равны ли два объекта типы. Однако есть ли способ узнать, равны ли два объекта, значение очень похоже на хэш-код в Java?
Вопрос о переполнении стека Есть ли в JavaScript какая-либо функция hashCode? похож на этот вопрос, но требует более академического ответа. Приведенный выше сценарий демонстрирует, почему он был бы необходим, и мне интересно, есть ли эквивалентное решение.
Обратите внимание, что даже в Java a.hashCode() == b.hashCode() не означает, что нет означает, что a равен b. Это необходимое условие, но не достаточное.
Если вам НЕОБХОДИМО сравнивать объекты в вашем коде, вы, вероятно, пишете свой код неправильно. Лучшим вопросом может быть: «Как я могу написать этот код, чтобы мне не приходилось сравнивать объекты?»
Попробуйте пакет объект-равный.
@ th317erd, не могли бы вы объяснить себя? ...
@ElMac Я не могу говорить напрямую от лица человека, но я согласен с утверждением, и мой мыслительный процесс таков, что очень часто объекты JS довольно большие. У вас редко есть что-то с очень небольшим количеством свойств, например person = { name: "fred", age: 42 }. Если вы это сделаете, и вам придется искать их по точному равенству, это кажется пустой тратой. Чаще всего ваши простые объекты по-прежнему будут иметь множество свойств - и одно из них - uinique, или вы хотите, чтобы оно было уникальным, например, какой-то идентификатор. Затем вы можете выполнить поиск по нему, и вам не нужно проверять, все ли соответствует.
Большинство комментаторов правы в том, что это не очень хороший метод. После этого поста я много экспериментировал с этим методом с переменным успехом. В конечном счете, это не очень хороший метод для сравнения объектов, и количество дополнительного кода, необходимого для его правильной работы, делает его не стоит того (просто используйте вместо этого простой компаратор глубоких объектов)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Зависит от того, что вы подразумеваете под равенством. И поэтому вы, как разработчик классов, должны определить их равенство.
Иногда используется один случай, когда два экземпляра считаются «равными», если они указывают на одно и то же место в памяти, но это не всегда то, что вам нужно. Например, если у меня есть класс Person, я мог бы считать два объекта Person «равными», если они имеют одинаковую фамилию, имя и номер социального страхования (даже если они указывают на разные места в памяти).
С другой стороны, мы не можем просто сказать, что два объекта равны, если значение каждого из их членов одинаково, поскольку иногда вы этого не хотите. Другими словами, для каждого класса разработчик класса должен определить, какие члены составляют «идентичность» объектов, и разработать соответствующий оператор равенства (будь то путем перегрузки оператора == или метода Equals).
Один выход - сказать, что два объекта равны, если у них одинаковый хэш. Однако затем вам нужно задаться вопросом, как рассчитывается хэш для каждого экземпляра. Возвращаясь к приведенному выше примеру Person, мы могли бы использовать эту систему, если бы хэш был вычислен путем просмотра значений полей «Имя», «Фамилия» и «Номер социального страхования». Вдобавок ко всему, мы полагаемся на качество метода хеширования (это огромная тема сама по себе, но достаточно сказать, что не все хеши создаются равными, а плохие методы хеширования могут привести к коллизиям более, которые в в этом случае будут возвращены ложные совпадения).
Вы пытаетесь проверить, равны ли два объекта? т.е. их свойства равны?
Если это так, вы, вероятно, заметили такую ситуацию:
var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"
вам, возможно, придется сделать что-то вроде этого:
function objectEquals(obj1, obj2) {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
if (!obj2.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
for (var i in obj2) {
if (obj2.hasOwnProperty(i)) {
if (!obj1.hasOwnProperty(i)) return false;
if (obj1[i] != obj2[i]) return false;
}
}
return true;
}
Очевидно, что эта функция может быть немного оптимизирована и иметь возможность выполнять глубокую проверку (для обработки вложенных объектов: var a = { foo : { fu : "bar" } }), но идею вы поняли.
Как указано FOR, вам, возможно, придется адаптировать это для своих собственных целей, например: разные классы могут иметь разные определения «равных». Если вы просто работаете с простыми объектами, вышеперечисленного может быть достаточно, в противном случае вам может пригодиться настраиваемая функция MyClass.equals().
Это долгий метод, но он полностью тестирует объекты без каких-либо предположений о порядке свойств в каждом объекте.
Если вы используете библиотеку JSON, вы можете закодировать каждый объект как JSON, а затем сравнить полученные строки на предмет равенства.
var obj1 = {test:"value"};
var obj2 = {test:"value2"};
alert(JSON.encode(obj1)===JSON.encode(obj2));
ПРИМЕЧАНИЕ. Хотя этот ответ будет работать во многих случаях, как отметили несколько человек в комментариях, это проблематично по разным причинам. Практически во всех случаях вы захотите найти более надежное решение.
Интересно, но, на мой взгляд, немного сложно. Например, можете ли вы на 100% гарантировать, что свойства объекта всегда будут генерироваться в одном и том же порядке?
Это хороший вопрос, и возникает другой: действительно ли два объекта с одинаковыми свойствами в разном порядке равны или нет? Я полагаю, это зависит от того, что вы подразумеваете под «равным».
Это определенно один из самых интересных ответов и полезная информация - спасибо, что передали ее!
Обратите внимание, что большинство кодировщиков и стрингификаторов игнорируют функции и преобразуют нескончаемые числа, такие как NaN, в null.
Я согласен с Гвидо, порядок свойств важен и не может быть гарантирован. @JoelAnair, я думаю, что два объекта с одинаковыми свойствами в разных порядках следует считать равными, если значения свойств равны.
Возможно, вы все еще сможете использовать строковую обработку JSON, если соедините obj1 с примесью obj2 в obj2 (глубокая копия). В качестве ответа я опубликовал рабочую демонстрацию этого.
@JoelAnair, у свойств нет порядка.
@ GuidoGarcía @Juzer JSON.stringify не гарантирует порядок ключей. Документы Mozilla говорит: «Не гарантируется, что свойства объектов, не являющихся массивами, будут преобразованы в строку в каком-либо конкретном порядке. Не полагайтесь на упорядочение свойств внутри одного и того же объекта в рамках преобразования в строку».
Этот мог работает с альтернативным стрингификатором JSON, который последовательно сортирует ключи объектов.
Это сложно, так как вы можете добавить какое-то свойство в текстовый ввод, а затем очистить его, и оно останется с someProperty: "", и это не сделает их одинаковыми - obj a = {} obj b = {}, затем obj a = {someProperty: ""}
Краткий ответ
Простой ответ: нет, не существует универсальных способов определения того, что один объект равен другому в том смысле, в котором вы имеете в виду. Исключение составляют случаи, когда вы строго думаете о том, что объект не имеет типа.
Длинный ответ
Это концепция метода Equals, который сравнивает два разных экземпляра объекта, чтобы указать, равны ли они на уровне значений. Однако конкретный тип определяет, как должен быть реализован метод Equals. Итеративного сравнения атрибутов, имеющих примитивные значения, может быть недостаточно, вполне могут быть атрибуты, которые не следует рассматривать как часть значения объекта. Например,
function MyClass(a, b)
{
var c;
this.getCLazy = function() {
if (c === undefined) c = a * b // imagine * is really expensive
return c;
}
}
В этом случае c на самом деле не важен для определения равенства любых двух экземпляров MyClass, важны только a и b. В некоторых случаях c может отличаться в зависимости от экземпляра и, тем не менее, не иметь значения при сравнении.
Обратите внимание, что эта проблема возникает, когда члены могут сами быть экземплярами типа, и каждый из них должен иметь средства определения равенства.
Еще больше усложняет ситуацию то, что в JavaScript стирается различие между данными и методом.
Объект может ссылаться на метод, который должен вызываться как обработчик событий, и это, скорее всего, не будет считаться частью его «состояния значения». В то время как другому объекту вполне может быть назначена функция, которая выполняет важные вычисления и тем самым отличает этот экземпляр от других просто потому, что он ссылается на другую функцию.
Как насчет объекта, у которого один из существующих методов прототипа переопределен другой функцией? Может ли он все еще считаться равным другому экземпляру, если в остальном он идентичен? На этот вопрос можно ответить только в каждом конкретном случае для каждого типа.
Как указывалось ранее, исключение будет строго безтиповым объектом. В этом случае единственный разумный выбор - итеративное и рекурсивное сравнение каждого члена. Даже тогда нужно спросить, каково «значение» функции?
Если вы используете подчеркивание, вы можете просто сделать _.isEqual(obj1, obj2);
@Harsh, ответ не дал никакого решения, потому что его нет. Даже в Java нет серебряной пули для сравнения на предмет равенства объектов, и правильно реализовать метод .equals нетривиально, поэтому в Эффективная Java есть такая тема.
@Kumar Harsh, То, что делает два объекта равными, очень зависит от приложения; не обязательно принимать во внимание каждое свойство объекта, поэтому перебор каждого свойства объекта также не является конкретным решением.
Погуглил javascript equality object, получил ответ tl; dr, взял однострочный комментарий из комментария @chovy. Спасибо
Если вы используете angular, у вас angular.equals
В Node.js вы можете использовать его собственный assert.deepEqual, см. Мой ответ ниже stackoverflow.com/questions/201183/…
Не знаю, как этот ответ помогает любому разработчику, пытающемуся найти, как решить эту проблему, вместо того, чтобы знать, почему это проблема. Это похоже на ту же аналогию, если на моем ноутбуке часто возникают BSOD, должен ли я изучить HAL или ядро, чтобы понять, почему?
я думаю, вам стоит попробовать с stackoverflow.com/a/46645497/4419353
Если попытка сравнить адрес в памяти, чтобы увидеть, указывают ли 2 дескриптора на одну и ту же «вещь», если есть 2 идентичные копии объекта в 2 разных адресах памяти без какого-либо отслеживания, что-то действительно не так ...
Что такое подчеркивание? Это библиотека? Каков минимальный размер фрагмента кода для проверки равенства объектов?
Я бы посоветовал не использовать хеширование или сериализацию (как предлагает решение JSON). Если вам нужно проверить, равны ли два объекта, вам нужно определить, что означает равенство. Может случиться так, что все элементы данных в обоих объектах совпадают, или это может быть так, что должны совпадать ячейки памяти (что означает, что обе переменные ссылаются на один и тот же объект в памяти), или может быть, что только один член данных в каждом объекте должен совпадать.
Недавно я разработал объект, конструктор которого создает новый идентификатор (начиная с 1 и увеличиваясь на 1) каждый раз при создании экземпляра. Этот объект имеет функцию isEqual, которая сравнивает это значение id со значением id другого объекта и возвращает истину, если они совпадают.
В этом случае я определил «равно» как означающее совпадение значений идентификатора. Учитывая, что каждый экземпляр имеет уникальный идентификатор, это можно использовать для реализации идеи о том, что совпадающие объекты также занимают одно и то же место в памяти. Хотя в этом нет необходимости.
Оператор равенства по умолчанию в JavaScript для объектов возвращает значение true, если они ссылаются на одно и то же место в памяти.
var x = {};
var y = {};
var z = x;
x === y; // => false
x === z; // => true
Если вам нужен другой оператор равенства, вам нужно добавить метод equals(other) или что-то подобное в свои классы, и особенности вашей проблемной области будут определять, что именно это означает.
Вот пример игральной карты:
function Card(rank, suit) {
this.rank = rank;
this.suit = suit;
this.equals = function(other) {
return other.rank == this.rank && other.suit == this.suit;
};
}
var queenOfClubs = new Card(12, "C");
var kingOfSpades = new Card(13, "S");
queenOfClubs.equals(kingOfSpades); // => false
kingOfSpades.equals(new Card(13, "S")); // => true
Если объект (ы) можно преобразовать в строку JSON, это упрощает функцию equals ().
@scotts Не всегда. Преобразование объектов в JSON и сравнение строк может потребовать больших вычислительных ресурсов для сложных объектов в тесных циклах. Для простых объектов это, вероятно, не имеет большого значения, но на самом деле это действительно зависит от вашей конкретной ситуации. Правильное решение может заключаться в простом сравнении идентификаторов объектов или проверке каждого свойства, но его правильность полностью определяется предметной областью.
Разве мы не должны сравнивать и типы данных ?! return other.rank === this.rank && other.suit === this.suit;
@devsathish, наверное, нет. В JavaScript типы довольно быстрые и непостоянные, но если в вашем домене типы важны, вы также можете проверить типы.
@scotts Другая проблема с преобразованием в JSON заключается в том, что порядок свойств в строке становится значимым. {x:1, y:2}! == {y:2, x:1}
В JavaScript, конечно же, нет классов. Это новинка. Классы ES6 - это просто новый сахар. В противном случае это, вероятно, правильный ответ.
Мне нужно издеваться над запросами POST jQuery, поэтому для меня важно равенство того, что оба объекта имеют одинаковый набор свойств (ни один из них не отсутствует ни в одном из объектов), и что каждое значение свойства «равно» (согласно этому определению). Меня не волнуют объекты с несовпадающими методами.
Вот что я буду использовать, этого должно быть достаточно для требований мой конкретный:
function PostRequest() {
for (var i = 0; i < arguments.length; i += 2) {
this[arguments[i]] = arguments[i+1];
}
var compare = function(u, v) {
if (typeof(u) != typeof(v)) {
return false;
}
var allkeys = {};
for (var i in u) {
allkeys[i] = 1;
}
for (var i in v) {
allkeys[i] = 1;
}
for (var i in allkeys) {
if (u.hasOwnProperty(i) != v.hasOwnProperty(i)) {
if ((u.hasOwnProperty(i) && typeof(u[i]) == 'function') ||
(v.hasOwnProperty(i) && typeof(v[i]) == 'function')) {
continue;
} else {
return false;
}
}
if (typeof(u[i]) != typeof(v[i])) {
return false;
}
if (typeof(u[i]) == 'object') {
if (!compare(u[i], v[i])) {
return false;
}
} else {
if (u[i] !== v[i]) {
return false;
}
}
}
return true;
};
this.equals = function(o) {
return compare(this, o);
};
return this;
}
Используйте так:
foo = new PostRequest('text', 'hello', 'html', '<p>hello</p>');
foo.equals({ html: '<p>hello</p>', text: 'hello' });
Нуждаясь в более общей функции сравнения объектов, чем было опубликовано, я приготовил следующее. Критика оценена ...
Object.prototype.equals = function(iObj) {
if (this.constructor !== iObj.constructor)
return false;
var aMemberCount = 0;
for (var a in this) {
if (!this.hasOwnProperty(a))
continue;
if (typeof this[a] === 'object' && typeof iObj[a] === 'object' ? !this[a].equals(iObj[a]) : this[a] !== iObj[a])
return false;
++aMemberCount;
}
for (var a in iObj)
if (iObj.hasOwnProperty(a))
--aMemberCount;
return aMemberCount ? false : true;
}
В итоге я использовал вариант этого. Спасибо за идею подсчета участников!
Будьте очень осторожны с модификацией Object.prototype - в подавляющем большинстве случаев это не рекомендуется (например, добавления появляются во всех циклах for..in). Может, рассмотрите Object.equals = function(aObj, bObj) {...}?
Зачем изобретать велосипед? Попробуйте Лодаш. Он имеет ряд обязательных функций, таких как равно().
_.isEqual(object, other);
Он будет проверять каждое значение ключа методом грубой силы - как и другие примеры на этой странице - с использованием ECMAScript 5 и собственных оптимизаций, если они доступны в браузере.
Примечание. Ранее этот ответ рекомендовал Underscore.js, но Lodash лучше справился с исправлением ошибок и устранением проблем с согласованностью.
Функция isEqual в Underscore очень хороша (но для ее использования вам нужно загрузить их библиотеку - около 3 КБ в сжатом виде).
если вы посмотрите, что еще дает вам подчеркивание, вы не пожалеете, что втянули его
Даже если вы не можете позволить себе использовать подчеркивание в качестве зависимости, извлеките функцию isEqual, выполните требования лицензии и двигайтесь дальше. Это, безусловно, наиболее полный тест на равенство, упомянутый в stackoverflow.
@DaleAnderson Ха-ха, и вот я снова здесь, смотрю на то же самое не потому, что я не могу использовать Underscore, а потому, что я на самом деле не хочу, и действительно хотел посмотреть на что-то вроде isEqual для «вдохновения».
Просто обсуждая это на работе, и проблема с _ в том, что _.isEqual (0, -0) === false и _.isEqual (NaN, NaN) === true, что нарушает наименьшее удивление, по крайней мере, для меня.
Существует форк Underscore под названием LoDash, и этот автор очень обеспокоен такими проблемами согласованности, как эта. Протестируйте с LoDash и посмотрите, что у вас получится.
Реализация isEqual в Underscore.js не работает для меня на двух объектах, которые имеют точно такие же свойства (как видно в Firebug и протестированы с помощью JSON.stringify) и выглядят следующим образом: {"from":70,"to":[1283,1301],"subject":"test","body":"test","bodyType":"text","priority":"high"}
@mckoss, вы можете использовать автономный модуль, если вам не нужна вся библиотека npmjs.com/package/lodash.isequal
Если вы используете webpack и ES6, вы можете просто импортировать функцию lodash, которую хотите использовать. например import { isEqual } from 'lodash'. Тогда ваш связанный JS будет включать только эту функцию и пропускать все, что вы не используете.
Также рассмотрите пакет глубоко равный, который работает даже с массивами объектов.
Как сказать "о, просто используйте пакет X!" полезный? Вы говорите человеку абстрагироваться и наращивать свой код без реального объяснения того, как на самом деле добраться до решения самостоятельно. Я не говорю, что не используйте пакеты и не предлагайте их, но экосистема JavaScript непостоянна, вам следует продвигать реальное понимание решения, а не просто способ его временно обойти.
Полезно считать два объекта равными, если они имеют одинаковые значения для всех свойств и рекурсивно для всех вложенных объектов и массивов. Я также считаю равными следующие два объекта:
var a = {p1: 1};
var b = {p1: 1, p2: undefined};
Точно так же массивы могут иметь «отсутствующие» элементы и неопределенные элементы. Я бы тоже относился к ним так же:
var c = [1, 2];
var d = [1, 2, undefined];
Функция, реализующая это определение равенства:
function isEqual(a, b) {
if (a === b) {
return true;
}
if (generalType(a) != generalType(b)) {
return false;
}
if (a == b) {
return true;
}
if (typeof a != 'object') {
return false;
}
// null != {}
if (a instanceof Object != b instanceof Object) {
return false;
}
if (a instanceof Date || b instanceof Date) {
if (a instanceof Date != b instanceof Date ||
a.getTime() != b.getTime()) {
return false;
}
}
var allKeys = [].concat(keys(a), keys(b));
uniqueArray(allKeys);
for (var i = 0; i < allKeys.length; i++) {
var prop = allKeys[i];
if (!isEqual(a[prop], b[prop])) {
return false;
}
}
return true;
}
Исходный код (включая вспомогательные функции, generalType и uniqueArray): Модульный тест и Test Runner здесь.
Если у вас есть под рукой функция глубокого копирования, вы можете использовать следующий трюк, чтобы по-прежнему использовал JSON.stringify при сопоставлении порядка свойств:
function equals(obj1, obj2) {
function _equals(obj1, obj2) {
return JSON.stringify(obj1)
=== JSON.stringify($.extend(true, {}, obj1, obj2));
}
return _equals(obj1, obj2) && _equals(obj2, obj1);
}
Демо: http://jsfiddle.net/CU3vb/3/
Обоснование:
Поскольку свойства obj1 копируются в клон одно за другим, их порядок в клоне сохраняется. И когда свойства obj2 копируются в клон, поскольку свойства, уже существующие в obj1, будут просто перезаписаны, их порядок в клоне будет сохранен.
Я не думаю, что сохранение порядка гарантировано в браузерах / движках.
@JoLiss Требуются цитаты;) Я помню, как тестировал это в нескольких браузерах и получал стабильные результаты. Но, конечно, никто не может гарантировать, что поведение останется неизменным в будущих браузерах / движках. В лучшем случае это уловка (как уже говорилось в ответе), и я не имел в виду, что это верный способ сравнения объектов.
Конечно, вот несколько указателей: В спецификации ECMAScript говорится, что объект "неупорядочен"; и этот ответ для фактического расхождения в поведении в текущих браузерах.
@JoLiss Спасибо за это! Но обратите внимание, я никогда не утверждал о сохранении порядка между кодом и скомпилированным объектом. Я требовал сохранения порядка свойств, значения которых заменяются на месте. Это был ключ к моему решению: использовать миксин для перезаписи значений свойств. Предполагая, что реализации обычно предпочитают использовать какую-то хэш-карту, замена только значений должна сохранять порядок ключей. Фактически это именно то, что я тестировал в разных браузерах.
О, конечно, теперь я понимаю, что вы имеете в виду. Хм. Это все еще достаточно неясно (и недостаточно определено), чтобы вызвать у меня тошноту, но я вижу, что на самом деле это может быть последовательным. Это определенно интересный прием.
@AtesGoral: можно ли сделать это ограничение немного больше явный (жирным шрифтом, ...). Большинство людей просто выполняют копировать вставить, не читая окружающий текст ...
Я написал небольшую библиотеку, работающую на Node.js и браузере под названием compare.js. Он предлагает обычные операторы сравнения, такие как ==,! =,>,> =, <, <= И идентичность для всех типов данных JavaScript.
Например, вы можете использовать
cmp.eq(obj1, obj2);
и это будет проверять равенство (используя подход глубокого равенства). В противном случае, если вы это сделаете
cmp.id(obj1, obj2);
он будет сравнивать по ссылке, следовательно, проверять идентичность. Вы также можете использовать <и> для объектов, что означает подмножество и надмножество.
compare.js охватывает около 700 модульных тестов, поэтому, будем надеяться, в нем не должно быть слишком много ошибок ;-).
Вы можете найти его на https://github.com/goloroden/compare.js бесплатно, он находится в открытом доступе под лицензией MIT.
Быстрый «взлом», чтобы определить, похожи ли два объекта, - это использовать их методы toString (). Если вы проверяете объекты A и B, убедитесь, что A и B имеют значимые методы toString (), и убедитесь, что строки, которые они возвращают, совпадают.
Это не панацея, но иногда может быть полезно в нужных ситуациях.
function isEqual(obj1, obj2){
type1 = typeof(obj1);
type2 = typeof(obj2);
if (type1===type2){
switch (type1){
case "object": return JSON.stringify(obj1)===JSON.stringify(obj2);
case "function": return eval(obj1).toString()===eval(obj2).toString();
default: return obj1==obj2;
}
}
return false;
}//have not tried but should work.
Где написано, что JSON.stringify всегда будет перечислять ключи в одном и том же порядке (особенно, если они не были добавлены в том же порядке к obj1 и obj2).
Это моя версия. Он использует новую функцию Object.keys, представленную в ES5, и идеи / тесты из +, + и +:
function objectEquals(x, y) {
'use strict';
if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
// after this just checking type of one would be enough
if (x.constructor !== y.constructor) { return false; }
// if they are functions, they should exactly refer to same one (because of closures)
if (x instanceof Function) { return x === y; }
// if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
if (x instanceof RegExp) { return x === y; }
if (x === y || x.valueOf() === y.valueOf()) { return true; }
if (Array.isArray(x) && x.length !== y.length) { return false; }
// if they are dates, they must had equal valueOf
if (x instanceof Date) { return false; }
// if they are strictly equal, they both need to be object at least
if (!(x instanceof Object)) { return false; }
if (!(y instanceof Object)) { return false; }
// recursive object equality check
var p = Object.keys(x);
return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
p.every(function (i) { return objectEquals(x[i], y[i]); });
}
///////////////////////////////////////////////////////////////
/// The borrowed tests, run them by clicking "Run code snippet"
///////////////////////////////////////////////////////////////
var printResult = function (x) {
if (x) { document.write('<div style = "color: green;">Passed</div>'); }
else { document.write('<div style = "color: red;">Failed</div>'); }
};
var assert = { isTrue: function (x) { printResult(x); }, isFalse: function (x) { printResult(!x); } }
assert.isTrue(objectEquals(null,null));
assert.isFalse(objectEquals(null,undefined));
assert.isFalse(objectEquals(/abc/, /abc/));
assert.isFalse(objectEquals(/abc/, /123/));
var r = /abc/;
assert.isTrue(objectEquals(r, r));
assert.isTrue(objectEquals("hi","hi"));
assert.isTrue(objectEquals(5,5));
assert.isFalse(objectEquals(5,10));
assert.isTrue(objectEquals([],[]));
assert.isTrue(objectEquals([1,2],[1,2]));
assert.isFalse(objectEquals([1,2],[2,1]));
assert.isFalse(objectEquals([1,2],[1,2,3]));
assert.isTrue(objectEquals({},{}));
assert.isTrue(objectEquals({a:1,b:2},{a:1,b:2}));
assert.isTrue(objectEquals({a:1,b:2},{b:2,a:1}));
assert.isFalse(objectEquals({a:1,b:2},{a:1,b:3}));
assert.isTrue(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assert.isFalse(objectEquals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}},{1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
Object.prototype.equals = function (obj) { return objectEquals(this, obj); };
var assertFalse = assert.isFalse,
assertTrue = assert.isTrue;
assertFalse({}.equals(null));
assertFalse({}.equals(undefined));
assertTrue("hi".equals("hi"));
assertTrue(new Number(5).equals(5));
assertFalse(new Number(5).equals(10));
assertFalse(new Number(1).equals("1"));
assertTrue([].equals([]));
assertTrue([1,2].equals([1,2]));
assertFalse([1,2].equals([2,1]));
assertFalse([1,2].equals([1,2,3]));
assertTrue(new Date("2011-03-31").equals(new Date("2011-03-31")));
assertFalse(new Date("2011-03-31").equals(new Date("1970-01-01")));
assertTrue({}.equals({}));
assertTrue({a:1,b:2}.equals({a:1,b:2}));
assertTrue({a:1,b:2}.equals({b:2,a:1}));
assertFalse({a:1,b:2}.equals({a:1,b:3}));
assertTrue({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}));
assertFalse({1:{name:"mhc",age:28}, 2:{name:"arb",age:26}}.equals({1:{name:"mhc",age:28}, 2:{name:"arb",age:27}}));
var a = {a: 'text', b:[0,1]};
var b = {a: 'text', b:[0,1]};
var c = {a: 'text', b: 0};
var d = {a: 'text', b: false};
var e = {a: 'text', b:[1,0]};
var i = {
a: 'text',
c: {
b: [1, 0]
}
};
var j = {
a: 'text',
c: {
b: [1, 0]
}
};
var k = {a: 'text', b: null};
var l = {a: 'text', b: undefined};
assertTrue(a.equals(b));
assertFalse(a.equals(c));
assertFalse(c.equals(d));
assertFalse(a.equals(e));
assertTrue(i.equals(j));
assertFalse(d.equals(k));
assertFalse(k.equals(l));
// from comments on stackoverflow post
assert.isFalse(objectEquals([1, 2, undefined], [1, 2]));
assert.isFalse(objectEquals([1, 2, 3], { 0: 1, 1: 2, 2: 3 }));
assert.isFalse(objectEquals(new Date(1234), 1234));
// no two different function is equal really, they capture their context variables
// so even if they have same toString(), they won't have same functionality
var func = function (x) { return true; };
var func2 = function (x) { return true; };
assert.isTrue(objectEquals(func, func));
assert.isFalse(objectEquals(func, func2));
assert.isTrue(objectEquals({ a: { b: func } }, { a: { b: func } }));
assert.isFalse(objectEquals({ a: { b: func } }, { a: { b: func2 } }));objectEquals([1,2,undefined],[1,2]) возвращает true
objectEquals([1,2,3],{0:1,1:2,2:3}) также возвращает true - например, нет проверки типа, только проверка ключа / значения.
objectEquals(new Date(1234),1234) возвращает true
Спасибо. Я рассмотрел ваши комментарии и добавил их в тесты.
objectEquals(/1234/,/asdf/) возвращает true
var x = {},y = {};x.a=x;y.a=y; objectEquals(x,y) выбрасывает Uncaught RangeError: Maximum call stack size exceeded, т. Е. Не работает при параллельных круговых ссылках.
Спасибо. Я исправил проблему с регулярным выражением, но не уверен, что можно сделать с циклическими ссылками.
Выполнение не проходит проверку valueOf, если не равно ... что может быть проблемой для альтернативных классов Number или String - или действительно для любого настраиваемого объекта или класса, который переопределяет valueOf.
Я никогда не видел альтернативных классов Number String. Java, например, что-то вроде String помечено как окончательное, чтобы предотвратить злоупотребления 1.
Вот один: mikemcl.github.io/bignumber.js. Его реализация valueOf выводит строковое представление числа. (Примечание: в случае этой библиотеки ваша функция не нарушена, поскольку свойства, определяющие значение, доступны независимо от значения, возвращаемого valueOf.)
if (x.constructor! == y.constructor) {return false; } Это могло бы сломаться при сравнении двух 'new String (' a ')' в разных окнах. Для равенства значений вам нужно будет проверить, есть ли String.isString на обоих объектах, а затем использовать свободную проверку равенства 'a == b'.
Между «ценностным» равенством и «строгим» равенством существует огромная разница, и их нельзя реализовывать одинаково. Равенство значений не должно заботиться о типах, кроме базовой структуры, которая является одной из следующих 4: «объект» (то есть набор пар ключ / значение), «число», «строка» или «массив». Вот и все. Все, что не является числом, строкой или массивом, следует сравнивать как набор пар ключ / значение, независимо от конструктора (кросс-оконный). При сравнении объектов приравнивайте значение буквальных чисел и экземпляров Number, но не приводите строки к числам.
Функция - это исключительный тип, который на самом деле не является «значением», кроме того, что он является указателем на некоторую логику, поэтому, если его вообще нужно сравнивать, его следует сравнивать на предмет строгого равенства экземпляров с другой функцией (т.е. они являются точно такая же функция).
Огромная проблема с сравнениями между окнами заключается в том, что такие функции, как String.isString (a), сообщают вам, что что-то является строкой, но если вы впоследствии вызовете a.equals, где вы определили equals в классе строк, это может быть undefined если 'a' пришло из другого, где это строка (литерал или экземпляр String), но для него не определено 'равно'.
Вот довольно чистая версия CoffeeScript того, как вы могли бы это сделать:
Object::equals = (other) ->
typeOf = Object::toString
return false if typeOf.call(this) isnt typeOf.call(other)
return `this == other` unless typeOf.call(other) is '[object Object]' or
typeOf.call(other) is '[object Array]'
(return false unless this[key].equals other[key]) for key, value of this
(return false if typeof this[key] is 'undefined') for key of other
true
Вот тесты:
describe "equals", ->
it "should consider two numbers to be equal", ->
assert 5.equals(5)
it "should consider two empty objects to be equal", ->
assert {}.equals({})
it "should consider two objects with one key to be equal", ->
assert {a: "banana"}.equals {a: "banana"}
it "should consider two objects with keys in different orders to be equal", ->
assert {a: "banana", kendall: "garrus"}.equals {kendall: "garrus", a: "banana"}
it "should consider two objects with nested objects to be equal", ->
assert {a: {fruit: "banana"}}.equals {a: {fruit: "banana"}}
it "should consider two objects with nested objects that are jumbled to be equal", ->
assert {a: {a: "banana", kendall: "garrus"}}.equals {a: {kendall: "garrus", a: "banana"}}
it "should consider two objects with arrays as values to be equal", ->
assert {a: ["apple", "banana"]}.equals {a: ["apple", "banana"]}
it "should not consider an object to be equal to null", ->
assert !({a: "banana"}.equals null)
it "should not consider two objects with different keys to be equal", ->
assert !({a: "banana"}.equals {})
it "should not consider two objects with different values to be equal", ->
assert !({a: "banana"}.equals {a: "grapefruit"})
что если кто-то перезапишет метод toString?
Лол, ну, я думаю, вы можете получить неожиданные результаты в зависимости от того, что вы сделали, чтобы отменить это.
Некоторые из следующих решений имеют проблемы с производительностью, функциональностью и стилем ... Они недостаточно продуманы, а некоторые из них не работают в разных случаях. Я попытался решить эту проблему в собственном решении и был бы очень признателен за ваш отзыв:
http://stamat.wordpress.com/javascript-object-comparison/
//Returns the object's class, Array, Date, RegExp, Object are of interest to us
var getClass = function(val) {
return Object.prototype.toString.call(val)
.match(/^\[object\s(.*)\]$/)[1];
};
//Defines the type of the value, extended typeof
var whatis = function(val) {
if (val === undefined)
return 'undefined';
if (val === null)
return 'null';
var type = typeof val;
if (type === 'object')
type = getClass(val).toLowerCase();
if (type === 'number') {
if (val.toString().indexOf('.') > 0)
return 'float';
else
return 'integer';
}
return type;
};
var compareObjects = function(a, b) {
if (a === b)
return true;
for (var i in a) {
if (b.hasOwnProperty(i)) {
if (!equal(a[i],b[i])) return false;
} else {
return false;
}
}
for (var i in b) {
if (!a.hasOwnProperty(i)) {
return false;
}
}
return true;
};
var compareArrays = function(a, b) {
if (a === b)
return true;
if (a.length !== b.length)
return false;
for (var i = 0; i < a.length; i++){
if (!equal(a[i], b[i])) return false;
};
return true;
};
var _equal = {};
_equal.array = compareArrays;
_equal.object = compareObjects;
_equal.date = function(a, b) {
return a.getTime() === b.getTime();
};
_equal.regexp = function(a, b) {
return a.toString() === b.toString();
};
// uncoment to support function as string compare
// _equal.fucntion = _equal.regexp;
/*
* Are two values equal, deep compare for objects and arrays.
* @param a {any}
* @param b {any}
* @return {boolean} Are equal?
*/
var equal = function(a, b) {
if (a !== b) {
var atype = whatis(a), btype = whatis(b);
if (atype === btype)
return _equal.hasOwnProperty(atype) ? _equal[atype](a, b) : a==b;
return false;
}
return true;
};
С этой функцией я делаю следующие предположения:
Это следует рассматривать как демонстрацию простой стратегии.
/**
* Checks the equality of two objects that contain primitive values. (ie. no nested objects, functions, etc.)
* @param {Object} object1
* @param {Object} object2
* @param {Boolean} [order_matters] Affects the return value of unordered objects. (ex. {a:1, b:2} and {b:2, a:1}).
* @returns {Boolean}
*/
function isEqual( object1, object2, order_matters ) {
var keys1 = Object.keys(object1),
keys2 = Object.keys(object2),
i, key;
// Test 1: Same number of elements
if ( keys1.length != keys2.length ) {
return false;
}
// If order doesn't matter isEqual({a:2, b:1}, {b:1, a:2}) should return true.
// keys1 = Object.keys({a:2, b:1}) = ["a","b"];
// keys2 = Object.keys({b:1, a:2}) = ["b","a"];
// This is why we are sorting keys1 and keys2.
if ( !order_matters ) {
keys1.sort();
keys2.sort();
}
// Test 2: Same keys
for( i = 0; i < keys1.length; i++ ) {
if ( keys1[i] != keys2[i] ) {
return false;
}
}
// Test 3: Values
for( i = 0; i < keys1.length; i++ ) {
key = keys1[i];
if ( object1[key] != object2[key] ) {
return false;
}
}
return true;
}
Если вы работаете в AngularJS, функция angular.equals определит, равны ли два объекта. В Ember.js используйте isEqual.
angular.equals - См. документы или источник для получения дополнительной информации об этом методе. Он также выполняет глубокое сравнение массивов.isEqual - Дополнительную информацию об этом методе см. В документы или источник. Он не выполняет глубокого сравнения массивов.var purple = [{"purple": "drank"}];
var drank = [{"purple": "drank"}];
if (angular.equals(purple, drank)) {
document.write('got dat');
}<script src = "https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>Для сравнения ключей для простых экземпляров объектов пар ключ / значение я использую:
function compareKeys(r1, r2) {
var nloops = 0, score = 0;
for(k1 in r1) {
for(k2 in r2) {
nloops++;
if (k1 == k2)
score++;
}
}
return nloops == (score * score);
};
После сравнения ключей достаточно простого дополнительного цикла for..in.
Сложность - O (N * N), где N - количество ключей.
Я надеюсь / предполагаю, что объекты, которые я определяю, не будут содержать более 1000 свойств ...
Это дополнение ко всему вышесказанному, а не замена. Если вам нужно ускорить мелкое сравнение объектов без необходимости проверять лишние рекурсивные случаи. Вот снимок.
Это сравнивается для: 1) равенства количества собственных свойств, 2) равенства имен ключей, 3) если bCompareValues == true, равенства соответствующих значений свойств и их типов (тройное равенство)
var shallowCompareObjects = function(o1, o2, bCompareValues) {
var s,
n1 = 0,
n2 = 0,
b = true;
for (s in o1) { n1 ++; }
for (s in o2) {
if (!o1.hasOwnProperty(s)) {
b = false;
break;
}
if (bCompareValues && o1[s] !== o2[s]) {
b = false;
break;
}
n2 ++;
}
return b && n1 == n2;
}
Проверка на равенство объектов: JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())
Вышеупомянутый тест также работает с массивами объектов, в этом случае используйте функцию сортировки, как описано в http://www.w3schools.com/jsref/jsref_sort.asp
Может хватить для небольших массивов с плоскими схемами JSON.
Не гарантируется работа - свойства объекта неупорядочены [конкретный порядок никогда не гарантируется] в соответствии с Спецификация EcmaScript 3.
множество проблем см. stackoverflow.com/a/201305/3780629
Если вы сравниваете объекты JSON, вы можете использовать https://github.com/mirek/node-rus-diff
npm install rus-diff
Использование:
a = {foo:{bar:1}}
b = {foo:{bar:1}}
c = {foo:{bar:2}}
var rusDiff = require('rus-diff').rusDiff
console.info(rusDiff(a, b)) // -> false, meaning a and b are equal
console.info(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }
Если два объекта различны, возвращается объект типа {$rename:{...}, $unset:{...}, $set:{...}}, совместимый с MongoDB.
Вот очень простой подход к проверке «равенства значений» объекта.
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different, objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal, objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects are considered equivalent
return true;
}
// Outputs: true
console.info(isEquivalent(john, bobby));
Как видите, чтобы проверить «равенство значений» объектов, нам, по сути, нужно перебрать каждое свойство в объектах, чтобы увидеть, равны ли они. И хотя эта простая реализация работает в нашем примере, во многих случаях она не обрабатывается. Например:
Для надежного метода проверки «равенства значений» объектов лучше полагаться на хорошо протестированную библиотеку, которая охватывает различные граничные случаи, такие как Нижнее подчеркивание.
var john = {
occupation: "Web Developer",
age: 25
};
var bobby = {
occupation: "Web Developer",
age: 25
};
// Outputs: true
console.info(_.isEqual(john, bobby));
не обрабатывает вложенные объекты
В Node.js вы можете использовать собственный require("assert").deepStrictEqual. Больше информации:
http://nodejs.org/api/assert.html
Например:
var assert = require("assert");
assert.deepStrictEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError
Другой пример, который возвращает true / false вместо возврата ошибок:
var assert = require("assert");
function deepEqual(a, b) {
try {
assert.deepEqual(a, b);
} catch (error) {
if (error.name === "AssertionError") {
return false;
}
throw error;
}
return true;
};
Chai также имеет эту функцию. В этом случае вы будете использовать: var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;.
Некоторые версии Node.js устанавливают error.name на "AssertionError [ERR_ASSERTION]". В этом случае я бы заменил оператор if на if (error.code === 'ERR_ASSERTION') {.
Я понятия не имел, что deepStrictEqual - лучший вариант. Я ломал голову, пытаясь понять, почему strictEqual не работает. Фантастический.
Конечно, пока мы занимаемся этим, я предложу свое собственное изобретение колеса (я горжусь количеством спиц и используемых материалов):
////////////////////////////////////////////////////////////////////////////////
var equals = function ( objectA, objectB ) {
var result = false,
keysA,
keysB;
// Check if they are pointing at the same variable. If they are, no need to test further.
if ( objectA === objectB ) {
return true;
}
// Check if they are the same type. If they are not, no need to test further.
if ( typeof objectA !== typeof objectB ) {
return false;
}
// Check what kind of variables they are to see what sort of comparison we should make.
if ( typeof objectA === "object" ) {
// Check if they have the same constructor, so that we are comparing apples with apples.
if ( objectA.constructor === objectA.constructor ) {
// If we are working with Arrays...
if ( objectA instanceof Array ) {
// Check the arrays are the same length. If not, they cannot be the same.
if ( objectA.length === objectB.length ) {
// Compare each element. They must be identical. If not, the comparison stops immediately and returns false.
return objectA.every(
function ( element, i ) {
return equals( element, objectB[ i ] );
}
);
}
// They are not the same length, and so are not identical.
else {
return false;
}
}
// If we are working with RegExps...
else if ( objectA instanceof RegExp ) {
// Return the results of a string comparison of the expression.
return ( objectA.toString() === objectB.toString() );
}
// Else we are working with other types of objects...
else {
// Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.
keysA = Object.keys( objectA );
keysB = Object.keys( objectB );
// Check the key arrays are the same length. If not, they cannot be the same.
if ( keysA.length === keysB.length ) {
// Compare each property. They must be identical. If not, the comparison stops immediately and returns false.
return keysA.every(
function ( element ) {
return equals( objectA[ element ], objectB[ element ] );
}
);
}
// They do not have the same number of keys, and so are not identical.
else {
return false;
}
}
}
// They don't have the same constructor.
else {
return false;
}
}
// If they are both functions, let us do a string comparison.
else if ( typeof objectA === "function" ) {
return ( objectA.toString() === objectB.toString() );
}
// If a simple variable type, compare directly without coercion.
else {
return ( objectA === objectB );
}
// Return a default if nothing has already been returned.
return result;
};
////////////////////////////////////////////////////////////////////////////////
Он возвращает false как можно быстрее, но, конечно, для большого объекта, где разница глубоко вложена, это может быть менее эффективным. В моем собственном сценарии важна хорошая обработка вложенных массивов.
Надеюсь, это поможет кому-то, кому нужно такое «колесо».
Простое решение этой проблемы, которое многие люди не осознают, - это отсортировать строки JSON (по символам). Это также обычно быстрее, чем другие упомянутые здесь решения:
function areEqual(obj1, obj2) {
var a = JSON.stringify(obj1), b = JSON.stringify(obj2);
if (!a) a = '';
if (!b) b = '';
return (a.split('').sort().join('') == b.split('').sort().join(''));
}
Еще одна полезная вещь в этом методе - вы можете фильтровать сравнения, передавая функцию «replacer» функциям JSON.stringify (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter). Следующее будет сравнивать только все ключи объектов с именем "derp":
function areEqual(obj1, obj2, filter) {
var a = JSON.stringify(obj1, filter), b = JSON.stringify(obj2, filter);
if (!a) a = '';
if (!b) b = '';
return (a.split('').sort().join('') == b.split('').sort().join(''));
}
var equal = areEqual(obj1, obj2, function(key, value) {
return (key === 'derp') ? value : undefined;
});
Да, я тоже забыл, но эту функцию можно ускорить, сначала протестировав приравнивание объекта и досрочное отключение, если это один и тот же объект: if (obj1 === obj2) return true;
Тогда areEqual({a: 'b'}, {b: 'a'}) получит true?
Да, после публикации я понял, что у этого "решения" есть проблемы. Для правильной работы алгоритма сортировки требуется немного больше работы.
Я знаю, что это немного устарело, но я хотел бы добавить решение, которое я придумал для этой проблемы. У меня был объект, и я хотел знать, когда его данные изменились. «что-то похожее на Object.observe», и я сделал следующее:
function checkObjects(obj,obj2){
var values = [];
var keys = [];
keys = Object.keys(obj);
keys.forEach(function(key){
values.push(key);
});
var values2 = [];
var keys2 = [];
keys2 = Object.keys(obj2);
keys2.forEach(function(key){
values2.push(key);
});
return (values == values2 && keys == keys2)
}
Здесь это можно продублировать и создать другой набор массивов для сравнения значений и ключей. Это очень просто, потому что теперь они являются массивами и вернут false, если объекты имеют разные размеры.
Это всегда будет возвращать false, потому что массивы не сравниваются по значению, например. [1,2] != [1,2].
Да, еще один ответ ...
Object.prototype.equals = function (object) {
if (this.constructor !== object.constructor) return false;
if (Object.keys(this).length !== Object.keys(object).length) return false;
var obk;
for (obk in object) {
if (this[obk] !== object[obk])
return false;
}
return true;
}
var aaa = JSON.parse('{"name":"mike","tel":"1324356584"}');
var bbb = JSON.parse('{"tel":"1324356584","name":"mike"}');
var ccc = JSON.parse('{"name":"mike","tel":"584"}');
var ddd = JSON.parse('{"name":"mike","tel":"1324356584", "work":"nope"}');
$("#ab").text(aaa.equals(bbb));
$("#ba").text(bbb.equals(aaa));
$("#bc").text(bbb.equals(ccc));
$("#ad").text(aaa.equals(ddd));<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
aaa equals bbb? <span id = "ab"></span> <br/>
bbb equals aaa? <span id = "ba"></span> <br/>
bbb equals ccc? <span id = "bc"></span> <br/>
aaa equals ddd? <span id = "ad"></span>не обрабатывает вложенные объекты
Вытаскивание из личной библиотеки, которую я постоянно использую в своей работе. Следующая функция - это мягкое рекурсивное глубокое равенство, которое не проверяет
В основном я использую это, чтобы проверить, получаю ли я одинаковые ответы на различные реализации API. Где могут возникнуть различия в реализации (например, строка и число) и дополнительные нулевые значения.
Его реализация довольно проста и коротка (если убрать все комментарии).
/** Recursively check if both objects are equal in value
***
*** This function is designed to use multiple methods from most probable
*** (and in most cases) valid, to the more regid and complex method.
***
*** One of the main principles behind the various check is that while
*** some of the simpler checks such as == or JSON may cause false negatives,
*** they do not cause false positives. As such they can be safely run first.
***
*** # !Important Note:
*** as this function is designed for simplified deep equal checks it is not designed
*** for the following
***
*** - Class equality, (ClassA().a = 1) maybe valid to (ClassB().b = 1)
*** - Inherited values, this actually ignores them
*** - Values being strictly equal, "1" is equal to 1 (see the basic equality check on this)
*** - Performance across all cases. This is designed for high performance on the
*** most probable cases of == / JSON equality. Consider bench testing, if you have
*** more 'complex' requirments
***
*** @param objA : First object to compare
*** @param objB : 2nd object to compare
*** @param .... : Any other objects to compare
***
*** @returns true if all equals, or false if invalid
***
*** @license Copyright by [email protected], 2012.
*** Licensed under the MIT license: http://opensource.org/licenses/MIT
**/
function simpleRecusiveDeepEqual(objA, objB) {
// Multiple comparision check
//--------------------------------------------
var args = Array.prototype.slice.call(arguments);
if (args.length > 2) {
for(var a=1; a<args.length; ++a) {
if (!simpleRecusiveDeepEqual(args[a-1], args[a])) {
return false;
}
}
return true;
} else if (args.length < 2) {
throw "simpleRecusiveDeepEqual, requires atleast 2 arguments";
}
// basic equality check,
//--------------------------------------------
// if this succed the 2 basic values is equal,
// such as numbers and string.
//
// or its actually the same object pointer. Bam
//
// Note that if string and number strictly equal is required
// change the equality from ==, to ===
//
if (objA == objB) {
return true;
}
// If a value is a bsic type, and failed above. This fails
var basicTypes = ["boolean", "number", "string"];
if ( basicTypes.indexOf(typeof objA) >= 0 || basicTypes.indexOf(typeof objB) >= 0 ) {
return false;
}
// JSON equality check,
//--------------------------------------------
// this can fail, if the JSON stringify the objects in the wrong order
// for example the following may fail, due to different string order:
//
// JSON.stringify( {a:1, b:2} ) == JSON.stringify( {b:2, a:1} )
//
if (JSON.stringify(objA) == JSON.stringify(objB)) {
return true;
}
// Array equality check
//--------------------------------------------
// This is performed prior to iteration check,
// Without this check the following would have been considered valid
//
// simpleRecusiveDeepEqual( { 0:1963 }, [1963] );
//
// Note that u may remove this segment if this is what is intended
//
if ( Array.isArray(objA) ) {
//objA is array, objB is not an array
if ( !Array.isArray(objB) ) {
return false;
}
} else if ( Array.isArray(objB) ) {
//objA is not array, objB is an array
return false;
}
// Nested values iteration
//--------------------------------------------
// Scan and iterate all the nested values, and check for non equal values recusively
//
// Note that this does not check against null equality, remove the various "!= null"
// if this is required
var i; //reuse var to iterate
// Check objA values against objB
for (i in objA) {
//Protect against inherited properties
if (objA.hasOwnProperty(i)) {
if (objB.hasOwnProperty(i)) {
// Check if deep equal is valid
if (!simpleRecusiveDeepEqual( objA[i], objB[i] )) {
return false;
}
} else if (objA[i] != null) {
//ignore null values in objA, that objB does not have
//else fails
return false;
}
}
}
// Check if objB has additional values, that objA do not, fail if so
for (i in objB) {
if (objB.hasOwnProperty(i)) {
if (objB[i] != null && !objA.hasOwnProperty(i)) {
//ignore null values in objB, that objA does not have
//else fails
return false;
}
}
}
// End of all checks
//--------------------------------------------
// By reaching here, all iteration scans have been done.
// and should have returned false if it failed
return true;
}
// Sanity checking of simpleRecusiveDeepEqual
(function() {
if (
// Basic checks
!simpleRecusiveDeepEqual({}, {}) ||
!simpleRecusiveDeepEqual([], []) ||
!simpleRecusiveDeepEqual(['a'], ['a']) ||
// Not strict checks
!simpleRecusiveDeepEqual("1", 1) ||
// Multiple objects check
!simpleRecusiveDeepEqual( { a:[1,2] }, { a:[1,2] }, { a:[1,2] } ) ||
// Ensure distinction between array and object (the following should fail)
simpleRecusiveDeepEqual( [1963], { 0:1963 } ) ||
// Null strict checks
simpleRecusiveDeepEqual( 0, null ) ||
simpleRecusiveDeepEqual( "", null ) ||
// Last "false" exists to make the various check above easy to comment in/out
false
) {
alert("FATAL ERROR: simpleRecusiveDeepEqual failed basic checks");
} else {
//added this last line, for SO snippet alert on success
alert("simpleRecusiveDeepEqual: Passed all checks, Yays!");
}
})();Я не эксперт по Javascript, но вот одна простая попытка решить эту проблему. Я проверяю три вещи:
object, а также то, что это не null, потому что typeof null - это object.function deepEqual (first, second) {
// Not equal if either is not an object or is null.
if (!isObject(first) || !isObject(second) ) return false;
// If properties count is different
if (keys(first).length != keys(second).length) return false;
// Return false if any property value is different.
for(prop in first){
if (first[prop] != second[prop]) return false;
}
return true;
}
// Checks if argument is an object and is not null
function isObject(obj) {
return (typeof obj === "object" && obj != null);
}
// returns arrays of object keys
function keys (obj) {
result = [];
for(var key in obj){
result.push(key);
}
return result;
}
// Some test code
obj1 = {
name: 'Singh',
age: 20
}
obj2 = {
age: 20,
name: 'Singh'
}
obj3 = {
name: 'Kaur',
age: 19
}
console.info(deepEqual(obj1, obj2));
console.info(deepEqual(obj1, obj3));не обрабатывает вложенные объекты
Вот решение в ES6 / ES2015 с использованием функционального подхода:
const typeOf = x =>
({}).toString
.call(x)
.match(/\[object (\w+)\]/)[1]
function areSimilar(a, b) {
const everyKey = f => Object.keys(a).every(f)
switch(typeOf(a)) {
case 'Array':
return a.length === b.length &&
everyKey(k => areSimilar(a.sort()[k], b.sort()[k]));
case 'Object':
return Object.keys(a).length === Object.keys(b).length &&
everyKey(k => areSimilar(a[k], b[k]));
default:
return a === b;
}
}
Не работает, если порядок ключей объекта изменился.
Краткая функциональная реализация deepEqual:
function deepEqual(x, y) {
return (x && y && typeof x === 'object' && typeof y === 'object') ?
(Object.keys(x).length === Object.keys(y).length) &&
Object.keys(x).reduce(function(isEqual, key) {
return isEqual && deepEqual(x[key], y[key]);
}, true) : (x === y);
}
Редактировать: версия 2, с использованием подсказки стрелы и стрелочных функций ES6:
function deepEqual(x, y) {
const ok = Object.keys, tx = typeof x, ty = typeof y;
return x && y && tx === 'object' && tx === ty ? (
ok(x).length === ok(y).length &&
ok(x).every(key => deepEqual(x[key], y[key]))
) : (x === y);
}
Вы можете заменить reduce на every для упрощения.
@jib нет, он не мог.
@nonkertompf уверен, что мог: Object.keys(x).every(key => deepEqual(x[key], y[key])).
@jib Вы правы, это упрощено без предыдущего аргумента Value функции обратного вызова.
Это не удается, когда вы сравниваете две даты
Правда. Или регулярные выражения, или экземпляры классов (typeof x === 'function'). Чтобы преодолеть это, проверку x === y можно заменить на String(x) === String(y).
deepEqual ({}, []) возвращает истину
да, если вам небезразличен такой угловой корпус, некрасивое решение - заменить : (x === y) на : (x === y && (x != null && y != null || x.constructor === y.constructor))
Мне нравится этот ответ, но мне нужны некоторые изменения, чтобы стать лучше.
Даже при замене (x === y) на (x === y && (x != null && y != null || x.constructor === y.constructor)) функция (v2) по-прежнему возвращает истину (в NodeJS) при сравнении {} и [].
: (x === y) - не то место, где можно попытаться исправить сравнение {} и []. Вместо этого поместите && a.constructor === b.constructor в конце основного условия (т.е. перед ?).
Не работает, если порядок ключей объекта изменился.
Да, это делает @IsaacPak, вы, вероятно, прокомментировали неправильный ответ: P По крайней мере, насколько мне известно, функция не зависит от порядка. Сделал шуточный тест и работает со всеми возможными вариациями. gist.github.com/exapsy/762064fa04ac613f4976ab4c59492f39
Я использую эту функцию comparable для создания копий моих объектов, сопоставимых с JSON:
var comparable = o => (typeof o != 'object' || !o)? o :
Object.keys(o).sort().reduce((c, key) => (c[key] = comparable(o[key]), c), {});
// Demo:
var a = { a: 1, c: 4, b: [2, 3], d: { e: '5', f: null } };
var b = { b: [2, 3], c: 4, d: { f: null, e: '5' }, a: 1 };
console.info(JSON.stringify(comparable(a)));
console.info(JSON.stringify(comparable(b)));
console.info(JSON.stringify(comparable(a)) == JSON.stringify(comparable(b)));<div id = "div"></div>Пригодится при тестировании (в большинстве тестовых фреймворков есть функция is). Например.
is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');
Если обнаруживается различие, строки регистрируются, что делает различия заметными:
x must match y
got {"a":1,"b":{"0":2,"1":3},"c":7,"d":{"e":"5","f":null}},
expected {"a":1,"b":{"0":2,"1":3},"c":4,"d":{"e":"5","f":null}}.
хорошая идея (в моем случае сравниваемые объекты - это только пары ключ / значение, без особых вещей)
У меня есть гораздо более короткая функция, которая будет углубляться во все подобъекты или массивы. Он так же эффективен, как JSON.stringify(obj1) === JSON.stringify(obj2), но JSON.stringify не будет работать, если порядок отличается (как упоминалось здесь).
var obj1 = { a : 1, b : 2 };
var obj2 = { b : 2, a : 1 };
console.info(JSON.stringify(obj1) === JSON.stringify(obj2)); // false
Функция также будет хорошим началом, если вы хотите что-то сделать с неравными значениями.
function arr_or_obj(v)
{ return !!v && (v.constructor === Object || v.constructor === Array); }
function deep_equal(v1, v2)
{
if (arr_or_obj(v1) && arr_or_obj(v2) && v1.constructor === v2.constructor)
{
if (Object.keys(v1).length === Object.keys(v2).length) // check the length
for (var i in v1)
{
if (!deep_equal(v1[i], v2[i]))
{ return false; }
}
else
{ return false; }
}
else if (v1 !== v2)
{ return false; }
return true;
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
var obj1 = [
{
hat : {
cap : ['something', null ],
helmet : [ 'triple eight', 'pro-tec' ]
},
shoes : [ 'loafer', 'penny' ]
},
{
beers : [ 'budweiser', 'busch' ],
wines : [ 'barefoot', 'yellow tail' ]
}
];
var obj2 = [
{
shoes : [ 'loafer', 'penny' ], // same even if the order is different
hat : {
cap : ['something', null ],
helmet : [ 'triple eight', 'pro-tec' ]
}
},
{
beers : [ 'budweiser', 'busch' ],
wines : [ 'barefoot', 'yellow tail' ]
}
];
console.info(deep_equal(obj1, obj2)); // true
console.info(JSON.stringify(obj1) === JSON.stringify(obj2)); // false
console.info(deep_equal([], [])); // true
console.info(deep_equal({}, {})); // true
console.info(deep_equal([], {})); // falseИ если вы хотите добавить поддержку Function, Date и RegExp, вы можете добавить это в начале deep_equal (не тестировалось):
if ((typeof obj1 === 'function' && typeof obj2 === 'function') ||
(obj1 instanceof Date && obj2 instanceof Date) ||
(obj1 instanceof RegExp && obj2 instanceof RegExp))
{
obj1 = obj1.toString();
obj2 = obj2.toString();
}
Я столкнулся с той же проблемой и решил написать собственное решение. Но поскольку я хочу также сравнивать массивы с объектами и наоборот, я создал общее решение. Я решил добавить функции в прототип, но их можно легко переписать в отдельные функции. Вот код:
Array.prototype.equals = Object.prototype.equals = function(b) {
var ar = JSON.parse(JSON.stringify(b));
var err = false;
for(var key in this) {
if (this.hasOwnProperty(key)) {
var found = ar.find(this[key]);
if (found > -1) {
if (Object.prototype.toString.call(ar) === "[object Object]") {
delete ar[Object.keys(ar)[found]];
}
else {
ar.splice(found, 1);
}
}
else {
err = true;
break;
}
}
};
if (Object.keys(ar).length > 0 || err) {
return false;
}
return true;
}
Array.prototype.find = Object.prototype.find = function(v) {
var f = -1;
for(var i in this) {
if (this.hasOwnProperty(i)) {
if (Object.prototype.toString.call(this[i]) === "[object Array]" || Object.prototype.toString.call(this[i]) === "[object Object]") {
if (this[i].equals(v)) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
else if (this[i] === v) {
f = (typeof(i) == "number") ? i : Object.keys(this).indexOf(i);
}
}
}
return f;
}
Этот алгоритм разделен на две части; Сама функция equals и функция для поиска числового индекса свойства в массиве / объекте. Функция поиска нужна только потому, что indexof находит только числа и строки, но не объекты.
Это можно назвать так:
({a: 1, b: "h"}).equals({a: 1, b: "h"});
Функция возвращает либо истину, либо ложь, в данном случае истину. Алгоритм также позволяет сравнивать очень сложные объекты:
({a: 1, b: "hello", c: ["w", "o", "r", "l", "d", {answer1: "should be", answer2: true}]}).equals({b: "hello", a: 1, c: ["w", "d", "o", "r", {answer1: "should be", answer2: true}, "l"]})
Верхний пример вернет истину, даже если свойства имеют другой порядок. Одна небольшая деталь, на которую следует обратить внимание: этот код также проверяет один и тот же тип двух переменных, поэтому "3" не то же самое, что 3.
Моя версия, которая включает в себя цепочку того, где обнаружена разница, и в чем разница.
function DeepObjectCompare(O1, O2)
{
try {
DOC_Val(O1, O2, ['O1->O2', O1, O2]);
return DOC_Val(O2, O1, ['O2->O1', O1, O2]);
} catch(e) {
console.info(e.Chain);
throw(e);
}
}
function DOC_Error(Reason, Chain, Val1, Val2)
{
this.Reason=Reason;
this.Chain=Chain;
this.Val1=Val1;
this.Val2=Val2;
}
function DOC_Val(Val1, Val2, Chain)
{
function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }
if (typeof(Val1)!==typeof(Val2))
return DoThrow('Type Mismatch');
if (Val1===null || Val1===undefined)
return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;
if (Val1.constructor!==Val2.constructor)
return DoThrow('Constructor mismatch');
switch(typeof(Val1))
{
case 'object':
for(var m in Val1)
{
if (!Val1.hasOwnProperty(m))
continue;
var CurChain=Chain.concat([m]);
if (!Val2.hasOwnProperty(m))
return DoThrow('Val2 missing property', CurChain);
DOC_Val(Val1[m], Val2[m], CurChain);
}
return true;
case 'number':
if (Number.isNaN(Val1))
return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;
case 'string':
case 'boolean':
return Val1!==Val2 ? DoThrow('Value mismatch') : true;
case 'function':
if (Val1.prototype!==Val2.prototype)
return DoThrow('Prototype mismatch');
if (Val1!==Val2)
return DoThrow('Function mismatch');
return true;
default:
return DoThrow('Val1 is unknown type');
}
}
Просто хотел поделиться своей версией сравнения объектов с использованием некоторых функций es6. Заказ не учитывается. После преобразования всех if / else в тернарный я получил следующее:
function areEqual(obj1, obj2) {
return Object.keys(obj1).every(key => {
return obj2.hasOwnProperty(key) ?
typeof obj1[key] === 'object' ?
areEqual(obj1[key], obj2[key]) :
obj1[key] === obj2[key] :
false;
}
)
}
Не знаю, публиковал ли кто-нибудь что-нибудь подобное, но вот функция, которую я сделал для проверки равенства объектов.
function objectsAreEqual(a, b) {
for (var prop in a) {
if (a.hasOwnProperty(prop)) {
if (b.hasOwnProperty(prop)) {
if (typeof a[prop] === 'object') {
if (!objectsAreEqual(a[prop], b[prop])) return false;
} else {
if (a[prop] !== b[prop]) return false;
}
} else {
return false;
}
}
}
return true;
}
Кроме того, он рекурсивен, поэтому он также может проверять глубокое равенство, если вы это так называете.
небольшое исправление: перед просмотром каждого свойства в a и b добавьте эту проверку if (Object.getOwnPropertyNames (a) .length! == Object.getOwnPropertyNames (b) .length) return false
Очевидно, что правильная проверка равенства должна быть рекурсивной. Я думаю, что один из таких рекурсивных ответов должен быть правильным. Принятый ответ не дает кода и не помогает
Вот общая функция проверки равенства, которая получает массив элементов в качестве входных данных и сравнивает их друг с другом. Работает со всеми типами элементов.
const isEqual = function(inputs = []) {
// Checks an element if js object.
const isObject = function(data) {
return Object.prototype.toString.call(data) === '[object Object]';
};
// Sorts given object by its keys.
const sortObjectByKey = function(obj) {
const self = this;
if (!obj) return {};
return Object.keys(obj).sort().reduce((initialVal, item) => {
initialVal[item] = !Array.isArray(obj[item]) &&
typeof obj[item] === 'object'
? self.objectByKey(obj[item])
: obj[item];
return initialVal;
}, {});
};
// Checks equality of all elements in the input against each other. Returns true | false
return (
inputs
.map(
input =>
typeof input == 'undefined'
? ''
: isObject(input)
? JSON.stringify(sortObjectByKey(input))
: JSON.stringify(input)
)
.reduce(
(prevValue, input) =>
prevValue === '' || prevValue === input ? input : false,
''
) !== false
);
};
// Tests (Made with Jest test framework.)
test('String equality check', () => {
expect(isEqual(['murat'])).toEqual(true);
expect(isEqual(['murat', 'john', 'doe'])).toEqual(false);
expect(isEqual(['murat', 'murat', 'murat'])).toEqual(true);
});
test('Float equality check', () => {
expect(isEqual([7.89, 3.45])).toEqual(false);
expect(isEqual([7, 7.50])).toEqual(false);
expect(isEqual([7.50, 7.50])).toEqual(true);
expect(isEqual([7, 7])).toEqual(true);
expect(isEqual([0.34, 0.33])).toEqual(false);
expect(isEqual([0.33, 0.33])).toEqual(true);
});
test('Array equality check', () => {
expect(isEqual([[1, 2, 3], [1, 2, 3]])).toEqual(true);
expect(isEqual([[1, 3], [1, 2, 3]])).toEqual(false);
expect(isEqual([['murat', 18], ['murat', 18]])).toEqual(true);
});
test('Object equality check', () => {
let obj1 = {
name: 'murat',
age: 18
};
let obj2 = {
name: 'murat',
age: 18
};
let obj3 = {
age: 18,
name: 'murat'
};
let obj4 = {
name: 'murat',
age: 18,
occupation: 'nothing'
};
expect(isEqual([obj1, obj2])).toEqual(true);
expect(isEqual([obj1, obj2, obj3])).toEqual(true);
expect(isEqual([obj1, obj2, obj3, obj4])).toEqual(false);
});
test('Weird equality checks', () => {
expect(isEqual(['', {}])).toEqual(false);
expect(isEqual([0, '0'])).toEqual(false);
});
Я реализовал метод, который принимает два json-файла и проверяет, имеют ли их ключи одинаковые значения, используя рекурсию. Я использовал Другой вопрос, чтобы решить эту проблему.
const arraysEqual = (a, b) => {
if (a === b)
return true;
if (a === null || b === null)
return false;
if (a.length !== b.length)
return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i])
return false;
}
return true;
};
const jsonsEqual = (a, b) => {
if (typeof a !== 'object' || typeof b !== 'object')
return false;
if (Object.keys(a).length === Object.keys(b).length) { // if items have the same size
let response = true;
for (let key in a) {
if (!b[key]) // if not key
response = false;
if (typeof a[key] !== typeof b[key]) // if typeof doesn't equals
response = false;
else {
if (Array.isArray(a[key])) // if array
response = arraysEqual(a[key], b[key]);
else if (typeof a[key] === 'object') // if another json
response = jsonsEqual(a[key], b[key]);
else if (a[key] !== b[key]) // not equals
response = false;
}
if (!response) // return if one item isn't equal
return false;
}
} else
return false;
return true;
};
const json1 = {
a: 'a',
b: 'asd',
c: [
'1',
2,
2.5,
'3',
{
d: 'asd',
e: [
1.6,
{
f: 'asdasd',
g: '123'
}
]
}
],
h: 1,
i: 1.2,
};
const json2 = {
a: 'nops',
b: 'asd'
};
const json3 = {
a: 'h',
b: '484',
c: [
3,
4.5,
'2ss',
{
e: [
{
f: 'asdasd',
g: '123'
}
]
}
],
h: 1,
i: 1.2,
};
const result = jsonsEqual(json1,json2);
//const result = jsonsEqual(json1,json3);
//const result = jsonsEqual(json1,json1);
if (result) // is equal
$('#result').text("Jsons are the same")
else
$('#result').text("Jsons aren't equals")<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id = "result"></div>Решения Простейший и логичный для сравнения все вроде Объект, Массив, Строка, Инт ...
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
Примечание:
val1 и val2 на свой объектЯ предполагаю, что это не сработает во многих случаях, потому что порядок ключей в объектах не имеет значения - если JSON.stringify не переупорядочивает по алфавиту? (Кого я не могу найти задокументированный.)
да, вы правы ... для объекта вам нужно рекурсивно сортировать для обоих боковых объектов
Это не работает для объектов с круговыми ссылками.
Вот вариант трюка с строковым преобразованием, который требует меньше ввода и во многих случаях работает для тривиального сравнения данных JSON.
var obj1Fingerprint = JSON.stringify(obj1).replace(/\{|\}/g,'').split(',').sort().join(',');
var obj2Fingerprint = JSON.stringify(obj2).replace(/\{|\}/g,'').split(',').sort().join(',');
if ( obj1Fingerprint === obj2Fingerprint) { ... } else { ... }
не обрабатывает вложенные объекты
И очень ужасный след производительности. Плохая практика в целом: сначала нужно преобразовать для преобразования обоих этих объектов в строку, выделить в 4 раза больше памяти, а затем отсортировать обе строки, а затем сравнить строку этих объектов. Например, если бы мы были на другом языке, все бы сказали «какого хрена», но, очевидно, делать это в JS, это совершенно нормально, поскольку каждый отдельный ответ здесь включает хотя бы один «JSON.stringify».
Здесь много хороших мыслей! Вот моя версия глубокого равного. Я разместил его на github и написал несколько тестов. Трудно охватить все возможные случаи, а иногда и нет необходимости.
Я рассмотрел NaN !== NaN, а также циклические зависимости.
https://github.com/ryancat/simple-deep-equal/blob/master/index.js
вы можете использовать _.isEqual(obj1, obj2) из библиотеки underscore.js.
Вот пример:
var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true
См. Официальную документацию отсюда: http://underscorejs.org/#isEqual
Предполагая, что порядок свойств в объекте не изменился.
JSON.stringify () работает для глубоких и неглубоких объектов обоих типов, не очень уверенный в аспектах производительности:
var object1 = {
key: "value"
};
var object2 = {
key: "value"
};
var object3 = {
key: "no value"
};
console.info('object1 and object2 are equal: ', JSON.stringify(object1) === JSON.stringify(object2));
console.info('object2 and object3 are equal: ', JSON.stringify(object2) === JSON.stringify(object3));Это не делает то, что хочет OP, так как оно будет соответствовать только в том случае, если оба объекта имеют все одинаковые ключи, а они заявляют, что они не будут. Также потребуется, чтобы ключи были в одном порядке, что также не совсем разумно.
Какие свойства находятся в разном порядке ??? Нет, не лучший метод
В зависимости от. Если порядок ключей в объекте не имеет значения, и мне не нужно знать прототипы указанного объекта. Использование всегда делает работу.
const object = {};
JSON.stringify(object) === "{}" will pass but {} === "{}" will not
Я вижу ответы спагетти-кода. Без использования сторонних библиотек это очень просто.
Сначала отсортируйте два объекта по ключевым именам.
let objectOne = { hey, you }
let objectTwo = { you, hey }
// If you really wanted you could make this recursive for deep sort.
const sortObjectByKeyname = (objectToSort) => {
return Object.keys(objectToSort).sort().reduce((r, k) => (r[k] = objectToSort[k], r), {});
}
let objectOne = sortObjectByKeyname(objectOne)
let objectTwo = sortObjectByKeyname(objectTwo)
Затем просто используйте строку для их сравнения.
JSON.stringify(objectOne) === JSON.stringify(objectTwo)
Это также не работает для глубокого копирования, у него только одна глубина итерации.
Я думаю, что @andras означает, что вам нужно рекурсивно сортировать ключи вложенных объектов.
Итак, сначала вы рекурсивно сортируете объекты, затем выделяете другую часть памяти, чтобы буквально дублировать оба этих объекта и преобразовывать их в строки, а затем сравнивать их. Вы не видите в этом ничего плохого? Мол, а что, если это огромный объект? Предполагается, что JSON позволит вам проверять равенство без необходимости сначала сортировать, а затем сравнивать преобразованные строки. Очень плохие практики и плохая производительность, почему вы в первую очередь должны использовать JSON.stringify.
@eksapsy разные проблемы имеют разные решения. Однако сегодня удобочитаемость всегда превосходит производительность при кодировании; в современных системах у нас есть лишняя память, оптимизация - корень зла в программировании. Я уверен, что если у вас есть «огромные» объекты, то вы, вероятно, ошибаетесь всей архитектурой; вам следует подумать об использовании чего-то вроде Elastic для больших коллекций JSON. Однако есть много разных решений, я уверен, что это могло бы быть лучше, я написал это поспешно много лет назад.
let user1 = {
name: "John",
address: {
line1: "55 Green Park Road",
line2: {
a:[1,2,3]
}
},
email:null
}
let user2 = {
name: "John",
address: {
line1: "55 Green Park Road",
line2: {
a:[1,2,3]
}
},
email:null
}
// Method 1
function isEqual(a, b) {
return JSON.stringify(a) === JSON.stringify(b);
}
// Method 2
function isEqual(a, b) {
// checking type of a And b
if (typeof a !== 'object' || typeof b !== 'object') {
return false;
}
// Both are NULL
if (!a && !b ) {
return true;
} else if (!a || !b) {
return false;
}
let keysA = Object.keys(a);
let keysB = Object.keys(b);
if (keysA.length !== keysB.length) {
return false;
}
for(let key in a) {
if (!(key in b)) {
return false;
}
if (typeof a[key] === 'object') {
if (!isEqual(a[key], b[key]))
{
return false;
}
} else {
if (a[key] !== b[key]) {
return false;
}
}
}
return true;
}
console.info(isEqual(user1,user2));множество проблем см. stackoverflow.com/a/201305/3780629
Для тех из вас, кто использует Node, есть удобный метод isDeepStrictEqual в собственной библиотеке util, который может этого добиться.
const util = require('util');
const obj1 = {
foo: "bar",
baz: [1, 2]
};
const obj2 = {
foo: "bar",
baz: [1, 2]
};
obj1 == obj2 // false
util.isDeepStrictEqual(obj1, obj2) // true
https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2
его производительность должна быть хорошей. Не беспокойся. Даже я использую это в сложных сценариях. Когда мы используем это, нам не нужно беспокоиться о том, несет ли свойство объекта объект или массив. Json.Stringify все равно делает его строковым, и сравнение строк в javascript не имеет большого значения.
Спасибо за такой ответ! Он добавлен в Node v9
function isDeepEqual(obj1, obj2, testPrototypes = false) {
if (obj1 === obj2) {
return true
}
if (typeof obj1 === "function" && typeof obj2 === "function") {
return obj1.toString() === obj2.toString()
}
if (obj1 instanceof Date && obj2 instanceof Date) {
return obj1.getTime() === obj2.getTime()
}
if (
Object.prototype.toString.call(obj1) !==
Object.prototype.toString.call(obj2) ||
typeof obj1 !== "object"
) {
return false
}
const prototypesAreEqual = testPrototypes
? isDeepEqual(
Object.getPrototypeOf(obj1),
Object.getPrototypeOf(obj2),
true
)
: true
const obj1Props = Object.getOwnPropertyNames(obj1)
const obj2Props = Object.getOwnPropertyNames(obj2)
return (
obj1Props.length === obj2Props.length &&
prototypesAreEqual &&
obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
)
}
console.info(isDeepEqual({key: 'one'}, {key: 'first'}))
console.info(isDeepEqual({key: 'one'}, {key: 'one'}))Это классический вопрос на javascript! Я создал метод проверки глубокого равенства объектов с возможностью выбора свойств, которые следует игнорировать при сравнении. Аргументы - это два сравниваемых объекта, а также необязательный массив строковых относительных путей свойств, которые нужно игнорировать.
function isObjectEqual( o1, o2, ignorePropsArr=[]) {
// Deep Clone objects
let _obj1 = JSON.parse(JSON.stringify(o1)),
_obj2 = JSON.parse(JSON.stringify(o2));
// Remove props to ignore
ignorePropsArr.map( p => {
eval('_obj1.'+p+' = _obj2.'+p+' = "IGNORED"');
});
// compare as strings
let s1 = JSON.stringify(_obj1),
s2 = JSON.stringify(_obj2);
// return [s1==s2,s1,s2];
return s1==s2;
}
// Objects 0 and 1 are exact equals
obj0 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj1 = { price: 66544.10, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj2 = { price: 66544.12, RSIs: [0.000432334, 0.00046531], candles: {A: 543, B: 321, C: 4322}}
obj3 = { price: 66544.13, RSIs: [0.000432334, 0.00046531], candles: {A: 541, B: 321, C: 4322}}
obj4 = { price: 66544.14, RSIs: [0.000432334, 0.00046530], candles: {A: 543, B: 321, C: 4322}}
isObjectEqual(obj0,obj1) // true
isObjectEqual(obj0,obj2) // false
isObjectEqual(obj0,obj2,['price']) // true
isObjectEqual(obj0,obj3,['price']) // false
isObjectEqual(obj0,obj3,['price','candles.A']) // true
isObjectEqual(obj0,obj4,['price','RSIs[1]']) // true
ВНИМАНИЕ: чтобы уточнить, этот код БУДЕТ страдать, если свойства находятся в другом порядке. Я использовал модель для создания объектов, поэтому в моем случае это не было проблемой.
Это простая функция Javascript для сравнения двух объектов, имеющих простые пары ключ-значение. Функция вернет массив строк, где каждая строка - это путь к неравенству между двумя объектами.
function compare(a,b) {
var paths = [];
[...new Set(Object.keys(a).concat(Object.keys(b)))].forEach(key=>{
if (typeof a[key] === 'object' && typeof b[key] === 'object') {
var results = compare(a[key], b[key]);
if (JSON.stringify(results)!=='[]') {
paths.push(...results.map(result=>key.concat("=>"+result)));
}
}
else if (a[key]!==b[key]) {
paths.push(key);
}
})
return paths;
}
Если вы хотите сравнить только два объекта, не зная путей к неравенствам, вы можете сделать это следующим образом:
if (JSON.stringify(compare(object1, object2))==='[]') {
// the two objects are equal
} else {
// the two objects are not equal
}
ES6: Минимальный код, который я мог бы сделать, таков. Он выполняет глубокое сравнение рекурсивно, преобразуя весь отсортированный массив значений ключа, представляющий объект, единственное ограничение - никакие методы или символы не сравниваются.
const compareObjects = (a, b) => {
let s = (o) => Object.entries(o).sort().map(i => {
if (i[1] instanceof Object) i[1] = s(i[1]);
return i
})
return JSON.stringify(s(a)) === JSON.stringify(s(b))
}
console.info(compareObjects({b:4,a:{b:1}}, {a:{b:1},b:4}));ВАЖНЫЙ: Эта функция выполняет JSON.stringfy в МНОЖЕСТВО с отсортированными ключами и НЕТ в самом объекте:
Это полностью функциональный ответ, спасибо @Adriano Spadoni. Вы знаете, как получить измененный ключ / атрибут? Спасибо,
привет @digital, если вам нужны разные клавиши, это не идеальная функция. Проверьте другой ответ и используйте один с циклом по объектам.
Никогда не используйте JSON.stringify для сравнения объектов json. Не ожидается, что порядок ключей будет таким же.
Привет @Eksapsy, вот почему есть функция "sort ()", вы видите s (a) s (b)? эта функция хороша из-за сортировки. В этом примере есть ключи в другом, чтобы доказать, что он работает.
@digital, чтобы получить разницу, потребуется RegExp вместо «===», это выполнимо.
const one = {name:'mohit' , age:30};
//const two = {name:'mohit',age:30};
const two = {age:30,name:'mohit'};
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
console.info(isEquivalent(one,two))
можно хотя бы указать на источник adripofjavascript.com/blog/drips/…
сравнить строку JSON
function areTwoDictsEqual(dictA, dictB) {
function sortDict(dict) {
var keys = Object.keys(dict);
keys.sort();
var newDict = {};
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = dict[key];
newDict[key] = value;
}
return newDict;
}
return JSON.stringify(sortDict(dictA)) == JSON.stringify(sortDict(dictB));
}
После стольких поисков я нашел следующее рабочее решение
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different, objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal, objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects are considered equivalent
return true; }
Для получения дополнительной информации: Равенство объектов в JavaScript
Этот код не работает с объектами-аналогами примитивов; console.info(isEquivalent(new Number('3'), new Number('5'))); печатает true.
Хотя ответов на этот вопрос уже очень много. Моя попытка состоит в том, чтобы предоставить еще один способ реализовать это:
const primitveDataTypes = ['number', 'boolean', 'string', 'undefined'];
const isDateOrRegExp = (value) => value instanceof Date || value instanceof RegExp;
const compare = (first, second) => {
let agg = true;
if (typeof first === typeof second && primitveDataTypes.indexOf(typeof first) !== -1 && first !== second){
agg = false;
}
// adding support for Date and RegExp.
else if (isDateOrRegExp(first) || isDateOrRegExp(second)){
if (first.toString() !== second.toString()){
agg = false;
}
}
else {
if (Array.isArray(first) && Array.isArray(second)){
if (first.length === second.length){
for(let i = 0; i < first.length; i++){
if (typeof first[i] === 'object' && typeof second[i] === 'object'){
agg = compare(first[i], second[i]);
}
else if (first[i] !== second[i]){
agg = false;
}
}
} else {
agg = false;
}
} else {
const firstKeys = Object.keys(first);
const secondKeys = Object.keys(second);
if (firstKeys.length !== secondKeys.length){
agg = false;
}
for(let j = 0 ; j < firstKeys.length; j++){
if (firstKeys[j] !== secondKeys[j]){
agg = false;
}
if (first[firstKeys[j]] && second[secondKeys[j]] && typeof first[firstKeys[j]] === 'object' && typeof second[secondKeys[j]] === 'object'){
agg = compare(first[firstKeys[j]], second[secondKeys[j]]);
}
else if (first[firstKeys[j]] !== second[secondKeys[j]]){
agg = false;
}
}
}
}
return agg;
}
console.info('result', compare({a: 1, b: { c: [4, {d:5}, {e:6}]}, r: null}, {a: 1, b: { c: [4, {d:5}, {e:6}]}, r: 'ffd'})); //returns false.var object1 = {name: "humza" , gender : "male", age: 23}
var object2 = {name: "humza" , gender : "male", age: 23}
var result = Object.keys(object1).every((key) => object1[key] === object2[key])
Результатом будет правда, если object1 имеет такие же значения на object2.
Это не сработает, если у объекта object2 есть дополнительные ключи, которые объект object1 не включает.
Как определить, что частичный объект (Partial <T>) равен исходному объекту (T) в машинописном тексте.
function compareTwoObjects<T>(original: T, partial: Partial<T>): boolean {
return !Object.keys(partial).some((key) => partial[key] !== original[key]);
}
P.S. Изначально я планировал создать новый вопрос с ответом. Но такой вопрос уже существует и помечен как дублирующийся.
Я просто написал этот метод, чтобы быть уверенным, что массивы и объекты сравниваются четко.
Это тоже должно помочь! :)
public class Objects {
/**
* Checks whether a value is of type Object
* @param value the value
*/
public static isObject = (value: any): boolean => {
return value === Object(value) && Object.prototype.toString.call(value) !== '[object Array]'
}
/**
* Checks whether a value is of type Array
* @param value the value
*/
public static isArray = (value: any): boolean => {
return Object.prototype.toString.call(value) === '[object Array]' && !Objects.isObject(value)
}
/**
* Check whether two values are equal
*/
public static isEqual = (objectA: any, objectB: any) => {
// Objects
if (Objects.isObject(objectA) && !Objects.isObject(objectB)) {
return false
}
else if (!Objects.isObject(objectA) && Objects.isObject(objectB)) {
return false
}
// Arrays
else if (Objects.isArray(objectA) && !Objects.isArray(objectB)) {
return false
}
else if (!Objects.isArray(objectA) && Objects.isArray(objectB)) {
return false
}
// Primitives
else if (!Objects.isArray(objectA) && !Objects.isObject(objectA)) {
return objectA === objectB
}
// Object or array
else {
const compareObject = (objectA: any, objectB: any): boolean => {
if (Object.keys(objectA).length !== Object.keys(objectB).length) return false
for (const propertyName of Object.keys(objectA)) {
const valueA = objectA[propertyName]
const valueB = objectB[propertyName]
if (!Objects.isEqual(valueA, valueB)) {
return false
}
}
return true
}
const compareArray = (arrayA: any[], arrayB: any[]): boolean => {
if (arrayA.length !== arrayB.length) return false
for (const index in arrayA) {
const valueA = arrayA[index]
const valueB = arrayB[index]
if (!Objects.isEqual(valueA, valueB)) {
return false
}
}
return true
}
if (Objects.isObject(objectA)) {
return compareObject(objectA, objectB)
} else {
return compareArray(objectA, objectB)
}
}
}
}
Еще одна дополнительная опция - использовать equals из Библиотека Ramda:
const c = {a: 1, b: 2};
const d = {b: 2, a: 1};
R.equals(c, d); //=> true
const obj = {
name: "Carl",
age: 15
}
const obj2 = {
name: "Carl",
age: 15,
}
const compareObj = (objects) => {
const res = objects.map((item) => {
return Object.entries(item).flat().join()
})
return res.every((a) => {
return a === res[0]
})
}
console.info(compareObj([obj,obj2]))это не рекурсивно для внутренних элементов подобъекта
@LeninZapata, что ты имеешь в виду?
не работает для внутренних объектов и подобъектов
Также изучите этот вопрос stackoverflow.com/q/1068834/1671639