Как определить равенство для двух объектов JavaScript? - PullRequest
547 голосов
/ 14 октября 2008

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

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

Ответы [ 55 ]

16 голосов
/ 24 сентября 2014

В Node.js вы можете использовать его собственный require("assert").deepEqual. Больше информации: http://nodejs.org/api/assert.html

Например:

var assert = require("assert");
assert.deepEqual({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;
};
12 голосов
/ 06 марта 2016

Я использую эту 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.log(JSON.stringify(comparable(a)));
console.log(JSON.stringify(comparable(b)));
console.log(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}}.
9 голосов
/ 25 августа 2015

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

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

6 голосов
/ 05 марта 2017

Я не знаю, опубликовал ли кто-нибудь что-нибудь похожее на это, но вот функция, которую я сделал для проверки равенства объектов.

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

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

6 голосов
/ 20 марта 2018

вы можете использовать _.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

5 голосов
/ 10 февраля 2015

Простое решение этой проблемы, о котором многие даже не подозревают, заключается в сортировке строк 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;
});
4 голосов
/ 27 июня 2015

Если вы используете ES6 + через Babel или другим способом, вы также можете использовать Object.is(x, y).

Ссылка: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

3 голосов
/ 27 мая 2010

Нуждаясь в более общей функции сравнения объектов, чем было опубликовано, я подготовил следующее. Критика приветствуется ...

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;
}
3 голосов
/ 15 ноября 2016

Просто хотел представить свою версию сравнения объектов, используя некоторые функции 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;

        }
    )
}
3 голосов
/ 11 марта 2014

Если вы сравниваете объекты 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.log(rusDiff(a, b)) // -> false, meaning a and b are equal
console.log(rusDiff(a, c)) // -> { '$set': { 'foo.bar': 2 } }

Если два объекта различны, возвращается MongoDB-совместимый объект {$rename:{...}, $unset:{...}, $set:{...}}.

...