Как определить равенство двух объектов JavaScript?

Оператор строгого равенства сообщит вам, равны ли два объекта типы. Однако есть ли способ узнать, равны ли два объекта, значение очень похоже на хэш-код в Java?

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

Также изучите этот вопрос stackoverflow.com/q/1068834/1671639

Praveen 06.05.2014 13:48

Обратите внимание, что даже в Java a.hashCode() == b.hashCode() не означает, что нет означает, что a равен b. Это необходимое условие, но не достаточное.

Heinzi 24.09.2014 17:34

Если вам НЕОБХОДИМО сравнивать объекты в вашем коде, вы, вероятно, пишете свой код неправильно. Лучшим вопросом может быть: «Как я могу написать этот код, чтобы мне не приходилось сравнивать объекты?»

th317erd 24.02.2017 20:50

Попробуйте пакет объект-равный.

lavee_singh 21.03.2018 21:40

@ th317erd, не могли бы вы объяснить себя? ...

El Mac 14.05.2018 15:44

@ElMac Я не могу говорить напрямую от лица человека, но я согласен с утверждением, и мой мыслительный процесс таков, что очень часто объекты JS довольно большие. У вас редко есть что-то с очень небольшим количеством свойств, например person = { name: "fred", age: 42 }. Если вы это сделаете, и вам придется искать их по точному равенству, это кажется пустой тратой. Чаще всего ваши простые объекты по-прежнему будут иметь множество свойств - и одно из них - uinique, или вы хотите, чтобы оно было уникальным, например, какой-то идентификатор. Затем вы можете выполнить поиск по нему, и вам не нужно проверять, все ли соответствует.

VLAZ 15.05.2018 00:01

Большинство комментаторов правы в том, что это не очень хороший метод. После этого поста я много экспериментировал с этим методом с переменным успехом. В конечном счете, это не очень хороший метод для сравнения объектов, и количество дополнительного кода, необходимого для его правильной работы, делает его не стоит того (просто используйте вместо этого простой компаратор глубоких объектов)

th317erd 16.05.2018 22:03

Попробуйте пакет deep-equal NPM

vsync 09.07.2018 20:06
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
743
8
574 002
63
Перейти к ответу Данный вопрос помечен как решенный

Ответы 63

Зависит от того, что вы подразумеваете под равенством. И поэтому вы, как разработчик классов, должны определить их равенство.

Иногда используется один случай, когда два экземпляра считаются «равными», если они указывают на одно и то же место в памяти, но это не всегда то, что вам нужно. Например, если у меня есть класс 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().

Это долгий метод, но он полностью тестирует объекты без каких-либо предположений о порядке свойств в каждом объекте.

briancollins081 06.03.2020 18:48

Если вы используете библиотеку JSON, вы можете закодировать каждый объект как JSON, а затем сравнить полученные строки на предмет равенства.

var obj1 = {test:"value"};
var obj2 = {test:"value2"};

alert(JSON.encode(obj1)===JSON.encode(obj2));

ПРИМЕЧАНИЕ. Хотя этот ответ будет работать во многих случаях, как отметили несколько человек в комментариях, это проблематично по разным причинам. Практически во всех случаях вы захотите найти более надежное решение.

Интересно, но, на мой взгляд, немного сложно. Например, можете ли вы на 100% гарантировать, что свойства объекта всегда будут генерироваться в одном и том же порядке?

Guido 14.10.2008 19:00

Это хороший вопрос, и возникает другой: действительно ли два объекта с одинаковыми свойствами в разном порядке равны или нет? Я полагаю, это зависит от того, что вы подразумеваете под «равным».

Joel Anair 14.10.2008 22:47

Это определенно один из самых интересных ответов и полезная информация - спасибо, что передали ее!

user4903 16.10.2008 04:37

Обратите внимание, что большинство кодировщиков и стрингификаторов игнорируют функции и преобразуют нескончаемые числа, такие как NaN, в null.

Stephen Belanger 09.04.2011 03:20

Я согласен с Гвидо, порядок свойств важен и не может быть гарантирован. @JoelAnair, я думаю, что два объекта с одинаковыми свойствами в разных порядках следует считать равными, если значения свойств равны.

Juzer Ali 26.03.2012 11:34

Возможно, вы все еще сможете использовать строковую обработку JSON, если соедините obj1 с примесью obj2 в obj2 (глубокая копия). В качестве ответа я опубликовал рабочую демонстрацию этого.

Ates Goral 14.06.2012 23:24

@JoelAnair, у свойств нет порядка.

user663031 17.06.2013 17:25

@ GuidoGarcía @Juzer JSON.stringify не гарантирует порядок ключей. Документы Mozilla говорит: «Не гарантируется, что свойства объектов, не являющихся массивами, будут преобразованы в строку в каком-либо конкретном порядке. Не полагайтесь на упорядочение свойств внутри одного и того же объекта в рамках преобразования в строку».

Nathan 13.08.2014 23:50

Этот мог работает с альтернативным стрингификатором JSON, который последовательно сортирует ключи объектов.

Roy Tinker 29.10.2014 02:51

Это сложно, так как вы можете добавить какое-то свойство в текстовый ввод, а затем очистить его, и оно останется с someProperty: "", и это не сделает их одинаковыми - obj a = {} obj b = {}, затем obj a = {someProperty: ""}

Raz 16.06.2020 16:00
Ответ принят как подходящий

Краткий ответ

Простой ответ: нет, не существует универсальных способов определения того, что один объект равен другому в том смысле, в котором вы имеете в виду. Исключение составляют случаи, когда вы строго думаете о том, что объект не имеет типа.

Длинный ответ

Это концепция метода 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);

chovy 22.08.2013 01:38

@Harsh, ответ не дал никакого решения, потому что его нет. Даже в Java нет серебряной пули для сравнения на предмет равенства объектов, и правильно реализовать метод .equals нетривиально, поэтому в Эффективная Java есть такая тема.

lcn 25.09.2013 21:44

@Kumar Harsh, То, что делает два объекта равными, очень зависит от приложения; не обязательно принимать во внимание каждое свойство объекта, поэтому перебор каждого свойства объекта также не является конкретным решением.

sethro 18.01.2014 02:05

Погуглил javascript equality object, получил ответ tl; dr, взял однострочный комментарий из комментария @chovy. Спасибо

Andrea 23.04.2014 18:54

Если вы используете angular, у вас angular.equals

boatcoder 23.07.2014 17:36

В Node.js вы можете использовать его собственный assert.deepEqual, см. Мой ответ ниже stackoverflow.com/questions/201183/…

Rafael Xavier 24.09.2014 16:24

Не знаю, как этот ответ помогает любому разработчику, пытающемуся найти, как решить эту проблему, вместо того, чтобы знать, почему это проблема. Это похоже на ту же аналогию, если на моем ноутбуке часто возникают BSOD, должен ли я изучить HAL или ядро, чтобы понять, почему?

Soham Dasgupta 02.08.2017 12:08

я думаю, вам стоит попробовать с stackoverflow.com/a/46645497/4419353

Pratik Bhalodiya 09.10.2017 14:37

Если попытка сравнить адрес в памяти, чтобы увидеть, указывают ли 2 дескриптора на одну и ту же «вещь», если есть 2 идентичные копии объекта в 2 разных адресах памяти без какого-либо отслеживания, что-то действительно не так ...

Xerix 14.08.2020 14:39

Что такое подчеркивание? Это библиотека? Каков минимальный размер фрагмента кода для проверки равенства объектов?

Aaron Franke 18.12.2020 05:14

Я бы посоветовал не использовать хеширование или сериализацию (как предлагает решение 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 14.11.2012 22:28

@scotts Не всегда. Преобразование объектов в JSON и сравнение строк может потребовать больших вычислительных ресурсов для сложных объектов в тесных циклах. Для простых объектов это, вероятно, не имеет большого значения, но на самом деле это действительно зависит от вашей конкретной ситуации. Правильное решение может заключаться в простом сравнении идентификаторов объектов или проверке каждого свойства, но его правильность полностью определяется предметной областью.

Daniel X Moore 14.11.2012 23:32

Разве мы не должны сравнивать и типы данных ?! return other.rank === this.rank && other.suit === this.suit;

devsathish 28.10.2013 18:59

@devsathish, наверное, нет. В JavaScript типы довольно быстрые и непостоянные, но если в вашем домене типы важны, вы также можете проверить типы.

Daniel X Moore 29.10.2013 03:27

@scotts Другая проблема с преобразованием в JSON заключается в том, что порядок свойств в строке становится значимым. {x:1, y:2}! == {y:2, x:1}

Stijn de Witt 02.03.2016 00:51

В JavaScript, конечно же, нет классов. Это новинка. Классы ES6 - это просто новый сахар. В противном случае это, вероятно, правильный ответ.

superluminary 15.04.2016 15:44

Мне нужно издеваться над запросами 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;
}

В итоге я использовал вариант этого. Спасибо за идею подсчета участников!

NateS 27.08.2010 01:42

Будьте очень осторожны с модификацией Object.prototype - в подавляющем большинстве случаев это не рекомендуется (например, добавления появляются во всех циклах for..in). Может, рассмотрите Object.equals = function(aObj, bObj) {...}?

Roy Tinker 29.10.2014 03:37

Зачем изобретать велосипед? Попробуйте Лодаш. Он имеет ряд обязательных функций, таких как равно().

_.isEqual(object, other);

Он будет проверять каждое значение ключа методом грубой силы - как и другие примеры на этой странице - с использованием ECMAScript 5 и собственных оптимизаций, если они доступны в браузере.

Примечание. Ранее этот ответ рекомендовал Underscore.js, но Lodash лучше справился с исправлением ошибок и устранением проблем с согласованностью.

Функция isEqual в Underscore очень хороша (но для ее использования вам нужно загрузить их библиотеку - около 3 КБ в сжатом виде).

mckoss 28.08.2010 05:16

если вы посмотрите, что еще дает вам подчеркивание, вы не пожалеете, что втянули его

PandaWood 12.06.2012 10:01

Даже если вы не можете позволить себе использовать подчеркивание в качестве зависимости, извлеките функцию isEqual, выполните требования лицензии и двигайтесь дальше. Это, безусловно, наиболее полный тест на равенство, упомянутый в stackoverflow.

Dale Anderson 07.09.2012 14:15

@DaleAnderson Ха-ха, и вот я снова здесь, смотрю на то же самое не потому, что я не могу использовать Underscore, а потому, что я на самом деле не хочу, и действительно хотел посмотреть на что-то вроде isEqual для «вдохновения».

Camilo Martin 14.09.2012 05:20

Просто обсуждая это на работе, и проблема с _ в том, что _.isEqual (0, -0) === false и _.isEqual (NaN, NaN) === true, что нарушает наименьшее удивление, по крайней мере, для меня.

podperson 11.02.2013 21:47

Существует форк Underscore под названием LoDash, и этот автор очень обеспокоен такими проблемами согласованности, как эта. Протестируйте с LoDash и посмотрите, что у вас получится.

coolaj86 12.02.2013 22:32

Реализация isEqual в Underscore.js не работает для меня на двух объектах, которые имеют точно такие же свойства (как видно в Firebug и протестированы с помощью JSON.stringify) и выглядят следующим образом: {"from":70,"to":[1283,1301],"subject":"test","body":"test","‌​bodyType":"text","pr‌​iority":"high"}

AsGoodAsItGets 16.09.2014 20:05

@mckoss, вы можете использовать автономный модуль, если вам не нужна вся библиотека npmjs.com/package/lodash.isequal

Rob Fox 01.06.2015 11:20

Если вы используете webpack и ES6, вы можете просто импортировать функцию lodash, которую хотите использовать. например import { isEqual } from 'lodash'. Тогда ваш связанный JS будет включать только эту функцию и пропускать все, что вы не используете.

ndbroadbent 10.04.2017 11:36

Также рассмотрите пакет глубоко равный, который работает даже с массивами объектов.

Wilfred Hughes 06.06.2017 20:09

Как сказать "о, просто используйте пакет X!" полезный? Вы говорите человеку абстрагироваться и наращивать свой код без реального объяснения того, как на самом деле добраться до решения самостоятельно. Я не говорю, что не используйте пакеты и не предлагайте их, но экосистема JavaScript непостоянна, вам следует продвигать реальное понимание решения, а не просто способ его временно обойти.

zfrisch 23.01.2018 19:44

Полезно считать два объекта равными, если они имеют одинаковые значения для всех свойств и рекурсивно для всех вложенных объектов и массивов. Я также считаю равными следующие два объекта:

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, будут просто перезаписаны, их порядок в клоне будет сохранен.

Я не думаю, что сохранение порядка гарантировано в браузерах / движках.

Jo Liss 07.03.2013 21:46

@JoLiss Требуются цитаты;) Я помню, как тестировал это в нескольких браузерах и получал стабильные результаты. Но, конечно, никто не может гарантировать, что поведение останется неизменным в будущих браузерах / движках. В лучшем случае это уловка (как уже говорилось в ответе), и я не имел в виду, что это верный способ сравнения объектов.

Ates Goral 07.03.2013 23:58

Конечно, вот несколько указателей: В спецификации ECMAScript говорится, что объект "неупорядочен"; и этот ответ для фактического расхождения в поведении в текущих браузерах.

Jo Liss 08.03.2013 03:43

@JoLiss Спасибо за это! Но обратите внимание, я никогда не утверждал о сохранении порядка между кодом и скомпилированным объектом. Я требовал сохранения порядка свойств, значения которых заменяются на месте. Это был ключ к моему решению: использовать миксин для перезаписи значений свойств. Предполагая, что реализации обычно предпочитают использовать какую-то хэш-карту, замена только значений должна сохранять порядок ключей. Фактически это именно то, что я тестировал в разных браузерах.

Ates Goral 08.03.2013 04:13

О, конечно, теперь я понимаю, что вы имеете в виду. Хм. Это все еще достаточно неясно (и недостаточно определено), чтобы вызвать у меня тошноту, но я вижу, что на самом деле это может быть последовательным. Это определенно интересный прием.

Jo Liss 08.03.2013 08:48

@AtesGoral: можно ли сделать это ограничение немного больше явный (жирным шрифтом, ...). Большинство людей просто выполняют копировать вставить, не читая окружающий текст ...

Willem Van Onsem 19.05.2015 15:13

Я написал небольшую библиотеку, работающую на 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).

Ted Hopp 09.01.2014 19:50

Это моя версия. Он использует новую функцию 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

Roy Tinker 29.10.2014 03:00

objectEquals([1,2,3],{0:1,1:2,2:3}) также возвращает true - например, нет проверки типа, только проверка ключа / значения.

Roy Tinker 29.10.2014 03:07

objectEquals(new Date(1234),1234) возвращает true

Roy Tinker 29.10.2014 03:32

Спасибо. Я рассмотрел ваши комментарии и добавил их в тесты.

Ebrahim Byagowi 21.11.2014 19:39

objectEquals(/1234/,/asdf/) возвращает true

Roy Tinker 28.01.2015 01:55

var x = {},y = {};x.a=x;y.a=y; objectEquals(x,y) выбрасывает Uncaught RangeError: Maximum call stack size exceeded, т. Е. Не работает при параллельных круговых ссылках.

Roy Tinker 28.01.2015 02:17

Спасибо. Я исправил проблему с регулярным выражением, но не уверен, что можно сделать с циклическими ссылками.

Ebrahim Byagowi 28.01.2015 13:15

Выполнение не проходит проверку valueOf, если не равно ... что может быть проблемой для альтернативных классов Number или String - или действительно для любого настраиваемого объекта или класса, который переопределяет valueOf.

Roy Tinker 30.01.2015 21:17

Я никогда не видел альтернативных классов Number String. Java, например, что-то вроде String помечено как окончательное, чтобы предотвратить злоупотребления 1.

Ebrahim Byagowi 31.01.2015 11:29

Вот один: mikemcl.github.io/bignumber.js. Его реализация valueOf выводит строковое представление числа. (Примечание: в случае этой библиотеки ваша функция не нарушена, поскольку свойства, определяющие значение, доступны независимо от значения, возвращаемого valueOf.)

Roy Tinker 03.02.2015 03:18

if (x.constructor! == y.constructor) {return false; } Это могло бы сломаться при сравнении двух 'new String (' a ')' в разных окнах. Для равенства значений вам нужно будет проверить, есть ли String.isString на обоих объектах, а затем использовать свободную проверку равенства 'a == b'.

Triynko 08.11.2016 18:06

Между «ценностным» равенством и «строгим» равенством существует огромная разница, и их нельзя реализовывать одинаково. Равенство значений не должно заботиться о типах, кроме базовой структуры, которая является одной из следующих 4: «объект» (то есть набор пар ключ / значение), «число», «строка» или «массив». Вот и все. Все, что не является числом, строкой или массивом, следует сравнивать как набор пар ключ / значение, независимо от конструктора (кросс-оконный). При сравнении объектов приравнивайте значение буквальных чисел и экземпляров Number, но не приводите строки к числам.

Triynko 08.11.2016 18:23

Функция - это исключительный тип, который на самом деле не является «значением», кроме того, что он является указателем на некоторую логику, поэтому, если его вообще нужно сравнивать, его следует сравнивать на предмет строгого равенства экземпляров с другой функцией (т.е. они являются точно такая же функция).

Triynko 08.11.2016 18:26

Огромная проблема с сравнениями между окнами заключается в том, что такие функции, как String.isString (a), сообщают вам, что что-то является строкой, но если вы впоследствии вызовете a.equals, где вы определили equals в классе строк, это может быть undefined если 'a' пришло из другого, где это строка (литерал или экземпляр String), но для него не определено 'равно'.

Triynko 08.11.2016 18:35

Вот довольно чистая версия 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?

Atmocreations 28.11.2014 12:47

Лол, ну, я думаю, вы можете получить неожиданные результаты в зависимости от того, что вы сделали, чтобы отменить это.

Luke 24.12.2014 19:12

Некоторые из следующих решений имеют проблемы с производительностью, функциональностью и стилем ... Они недостаточно продуманы, а некоторые из них не работают в разных случаях. Я попытался решить эту проблему в собственном решении и был бы очень признателен за ваш отзыв:

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;
};

С этой функцией я делаю следующие предположения:

  1. Вы управляете объектами, которые сравниваете, и у вас есть только примитивные значения (т.е. не вложенные объекты, функции и т. д.).
  2. Ваш браузер поддерживает Object.keys.

Это следует рассматривать как демонстрацию простой стратегии.

/**
 * 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 - См. документы или источник для получения дополнительной информации об этом методе. Он также выполняет глубокое сравнение массивов.
  • Ember.js 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.

Roy Tinker 29.10.2014 03:43

множество проблем см. stackoverflow.com/a/201305/3780629

Ashtonian 01.09.2019 01:01

Если вы сравниваете объекты 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));

Демо - JSFiddle

Как видите, чтобы проверить «равенство значений» объектов, нам, по сути, нужно перебрать каждое свойство в объектах, чтобы увидеть, равны ли они. И хотя эта простая реализация работает в нашем примере, во многих случаях она не обрабатывается. Например:

  • Что, если одно из значений свойства само по себе является объектом?
  • Что, если одно из значений свойства - NaN (единственное значение в JavaScript, который не равен самому себе?)
  • Что делать, если a имеет свойство со значением undefined, а b не имеет это свойство (которое, таким образом, оценивается как неопределенное?)

Для надежного метода проверки «равенства значений» объектов лучше полагаться на хорошо протестированную библиотеку, которая охватывает различные граничные случаи, такие как Нижнее подчеркивание.

var john = {
    occupation: "Web Developer",
    age: 25
};

var bobby = {
    occupation: "Web Developer",
    age: 25
};

// Outputs: true
console.info(_.isEqual(john, bobby));

Демо - JSFiddle

не обрабатывает вложенные объекты

Ashtonian 01.09.2019 00:54

В 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;.

Folusho Oladipo 01.06.2017 22:51

Некоторые версии Node.js устанавливают error.name на "AssertionError [ERR_ASSERTION]". В этом случае я бы заменил оператор if на if (error.code === 'ERR_ASSERTION') {.

Knute Knudsen 10.03.2018 20:05

Я понятия не имел, что deepStrictEqual - лучший вариант. Я ломал голову, пытаясь понять, почему strictEqual не работает. Фантастический.

NetOperator Wibby 21.02.2020 21:23

Конечно, пока мы занимаемся этим, я предложу свое собственное изобретение колеса (я горжусь количеством спиц и используемых материалов):

////////////////////////////////////////////////////////////////////////////////

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;

th317erd 10.02.2015 21:51

Тогда areEqual({a: 'b'}, {b: 'a'}) получит true?

okm 13.07.2015 15:36

Да, после публикации я понял, что у этого "решения" есть проблемы. Для правильной работы алгоритма сортировки требуется немного больше работы.

th317erd 14.07.2015 22:59

Я знаю, что это немного устарело, но я хотел бы добавить решение, которое я придумал для этой проблемы. У меня был объект, и я хотел знать, когда его данные изменились. «что-то похожее на 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].

jib 06.03.2016 03:14

Да, еще один ответ ...

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>

не обрабатывает вложенные объекты

Ashtonian 01.09.2019 00:52

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

  • Равенство классов
  • Унаследованные значения
  • Ценит строгое равенство

В основном я использую это, чтобы проверить, получаю ли я одинаковые ответы на различные реализации 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, но вот одна простая попытка решить эту проблему. Я проверяю три вещи:

  1. Это object, а также то, что это не null, потому что typeof null - это object.
  2. Если количество свойств двух объектов одинаково? Если нет, то они не равны.
  3. Прокрутите свойства одного объекта и проверьте, имеет ли соответствующее свойство такое же значение во втором объекте.

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));

не обрабатывает вложенные объекты

Ashtonian 01.09.2019 00:54

Вот решение в 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;
  }
}

демо доступно здесь

Не работает, если порядок ключей объекта изменился.

Isaac Pak 09.01.2020 22:56

Краткая функциональная реализация 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 06.03.2016 03:05

@jib нет, он не мог.

nonkertompf 20.04.2016 17:01

@nonkertompf уверен, что мог: Object.keys(x).every(key => deepEqual(x[key], y[key])).

jib 20.04.2016 21:54

@jib Вы правы, это упрощено без предыдущего аргумента Value функции обратного вызова.

nonkertompf 28.04.2016 20:23

Это не удается, когда вы сравниваете две даты

Greg 02.04.2018 23:48

Правда. Или регулярные выражения, или экземпляры классов (typeof x === 'function'). Чтобы преодолеть это, проверку x === y можно заменить на String(x) === String(y).

atmin 04.04.2018 10:51

deepEqual ({}, []) возвращает истину

AlexMorley-Finch 04.07.2018 17:01

да, если вам небезразличен такой угловой корпус, некрасивое решение - заменить : (x === y) на : (x === y && (x != null && y != null || x.constructor === y.constructor))

atmin 16.07.2018 10:42

Мне нравится этот ответ, но мне нужны некоторые изменения, чтобы стать лучше.

Amin Adel 05.03.2019 14:28

Даже при замене (x === y) на (x === y && (x != null && y != null || x.constructor === y.constructor)) функция (v2) по-прежнему возвращает истину (в NodeJS) при сравнении {} и [].

derekbaker783 07.08.2019 19:37

: (x === y) - не то место, где можно попытаться исправить сравнение {} и []. Вместо этого поместите && a.constructor === b.constructor в конце основного условия (т.е. перед ?).

James Clark 24.11.2019 05:52

Не работает, если порядок ключей объекта изменился.

Isaac Pak 09.01.2020 22:59

Да, это делает @IsaacPak, вы, вероятно, прокомментировали неправильный ответ: P По крайней мере, насколько мне известно, функция не зависит от порядка. Сделал шуточный тест и работает со всеми возможными вариациями. gist.github.com/exapsy/762064fa04ac613f4976ab4c59492f39

Eksapsy 08.11.2020 23:02

Я использую эту функцию 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}}.

хорошая идея (в моем случае сравниваемые объекты - это только пары ключ / значение, без особых вещей)

mech 25.07.2016 10:36

У меня есть гораздо более короткая функция, которая будет углубляться во все подобъекты или массивы. Он так же эффективен, как 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

Hith 25.08.2018 11:22

Очевидно, что правильная проверка равенства должна быть рекурсивной. Я думаю, что один из таких рекурсивных ответов должен быть правильным. Принятый ответ не дает кода и не помогает

canbax 17.01.2019 08:42

Вот общая функция проверки равенства, которая получает массив элементов в качестве входных данных и сравнивает их друг с другом. Работает со всеми типами элементов.

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 не переупорядочивает по алфавиту? (Кого я не могу найти задокументированный.)

Bram Vanroy 21.12.2017 16:32

да, вы правы ... для объекта вам нужно рекурсивно сортировать для обоих боковых объектов

Pratik Bhalodiya 29.12.2017 09:02

Это не работает для объектов с круговыми ссылками.

Nate-Bit Int 25.05.2018 02:05

Вот вариант трюка с строковым преобразованием, который требует меньше ввода и во многих случаях работает для тривиального сравнения данных 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 { ... }

не обрабатывает вложенные объекты

Ashtonian 01.09.2019 00:53

И очень ужасный след производительности. Плохая практика в целом: сначала нужно преобразовать для преобразования обоих этих объектов в строку, выделить в 4 раза больше памяти, а затем отсортировать обе строки, а затем сравнить строку этих объектов. Например, если бы мы были на другом языке, все бы сказали «какого хрена», но, очевидно, делать это в JS, это совершенно нормально, поскольку каждый отдельный ответ здесь включает хотя бы один «JSON.stringify».

Eksapsy 08.11.2020 22:45

Здесь много хороших мыслей! Вот моя версия глубокого равного. Я разместил его на 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, так как оно будет соответствовать только в том случае, если оба объекта имеют все одинаковые ключи, а они заявляют, что они не будут. Также потребуется, чтобы ключи были в одном порядке, что также не совсем разумно.

SpeedOfRound 23.11.2018 21:09

Какие свойства находятся в разном порядке ??? Нет, не лучший метод

Vishal Sakaria 06.03.2019 19:01

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

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 31.03.2019 21:16

Я думаю, что @andras означает, что вам нужно рекурсивно сортировать ключи вложенных объектов.

Davi Lima 18.04.2019 10:01

Итак, сначала вы рекурсивно сортируете объекты, затем выделяете другую часть памяти, чтобы буквально дублировать оба этих объекта и преобразовывать их в строки, а затем сравнивать их. Вы не видите в этом ничего плохого? Мол, а что, если это огромный объект? Предполагается, что JSON позволит вам проверять равенство без необходимости сначала сортировать, а затем сравнивать преобразованные строки. Очень плохие практики и плохая производительность, почему вы в первую очередь должны использовать JSON.stringify.

Eksapsy 08.11.2020 22:26

@eksapsy разные проблемы имеют разные решения. Однако сегодня удобочитаемость всегда превосходит производительность при кодировании; в современных системах у нас есть лишняя память, оптимизация - корень зла в программировании. Я уверен, что если у вас есть «огромные» объекты, то вы, вероятно, ошибаетесь всей архитектурой; вам следует подумать об использовании чего-то вроде Elastic для больших коллекций JSON. Однако есть много разных решений, я уверен, что это могло бы быть лучше, я написал это поспешно много лет назад.

Oliver Dixon 09.11.2020 14:35

     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

Ashtonian 01.09.2019 00:58

Для тех из вас, кто использует 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 не имеет большого значения.

TrickOrTreat 23.01.2020 07:25

Спасибо за такой ответ! Он добавлен в Node v9

Viet 02.10.2020 17:46

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

ВНИМАНИЕ: чтобы уточнить, этот код БУДЕТ страдать, если свойства находятся в другом порядке. Я использовал модель для создания объектов, поэтому в моем случае это не было проблемой.

JohnPan 04.09.2019 14:25

Это простая функция 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 в МНОЖЕСТВО с отсортированными ключами и НЕТ в самом объекте:

  1. ["а", ["б", 1]]
  2. ["б", 4]

Это полностью функциональный ответ, спасибо @Adriano Spadoni. Вы знаете, как получить измененный ключ / атрибут? Спасибо,

digitai 01.04.2020 18:47

привет @digital, если вам нужны разные клавиши, это не идеальная функция. Проверьте другой ответ и используйте один с циклом по объектам.

Adriano Spadoni 02.04.2020 16:43

Никогда не используйте JSON.stringify для сравнения объектов json. Не ожидается, что порядок ключей будет таким же.

Eksapsy 08.11.2020 22:22

Привет @Eksapsy, вот почему есть функция "sort ()", вы видите s (a) s (b)? эта функция хороша из-за сортировки. В этом примере есть ключи в другом, чтобы доказать, что он работает.

Adriano Spadoni 12.11.2020 11:27

@digital, чтобы получить разницу, потребуется RegExp вместо «===», это выполнимо.

Adriano Spadoni 12.11.2020 11:33

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/…

Eksapsy 08.11.2020 22:38
  1. сортировать объекты (словарь)
  2. сравнить строку 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.

Sapphire_Brick 31.03.2020 04:13

Хотя ответов на этот вопрос уже очень много. Моя попытка состоит в том, чтобы предоставить еще один способ реализовать это:

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 не включает.

Ram Kumar 08.06.2020 07:44

Как определить, что частичный объект (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]))

это не рекурсивно для внутренних элементов подобъекта

Lenin Zapata 27.02.2021 23:29

@LeninZapata, что ты имеешь в виду?

Asking 28.02.2021 14:53

не работает для внутренних объектов и подобъектов

Lenin Zapata 28.02.2021 22:14

Другие вопросы по теме