Jak określić równość dla dwóch obiektów JavaScript?

Operator ścisłej równości powie, czy dwa typy obiektów są równe. Czy istnieje jednak sposób, aby stwierdzić, czy dwa obiekty są sobie równe, , podobnie jak wartość hash code w Javie?

Pytanie o przepełnienie stosu czy istnieje jakaś funkcja hashCode w JavaScript? jest podobny do tego pytania, ale wymaga bardziej akademickiej odpowiedzi. Powyższy scenariusz pokazuje, dlaczego konieczne byłoby posiadanie takiego, i zastanawiam się, czy istnieje jakiś odpowiednik rozwiązanie .

Author: Community, 2008-10-14

30 answers

Krótka odpowiedź

Prosta odpowiedź brzmi: Nie, Nie ma ogólnych środków do ustalenia, że obiekt jest równy innemu w sensie, o którym mówisz. Wyjątek stanowi sytuacja, gdy myśli się o obiekcie bez liter.

Długa odpowiedź

Jest to koncepcja metody Equals, która porównuje dwa różne instancje obiektu, aby wskazać, czy są one równe na poziomie wartości. Jednak do określonego typu należy zdefiniowanie, w jaki sposób Equals metoda powinna zostać wdrożona. Iteracyjne porównanie atrybutów, które mają prymitywne wartości, może nie wystarczyć, mogą również istnieć atrybuty, które nie powinny być uważane za część wartości obiektu. Na przykład,

 function MyClass(a, b)
 {
     var c;
     this.getCLazy = function() {
         if (c === undefined) c = a * b // imagine * is really expensive
         return c;
     }
  }

W powyższym przypadku, c nie jest tak naprawdę ważne, aby określić, czy dowolne dwa instancje MyClass są sobie równe, tylko a i b są ważne. W niektórych przypadkach c mogą się różnić między instancjami, a jednak nie są istotne podczas porównywania.

Zwróć uwagę na to problem ma zastosowanie, gdy członkowie mogą sami być instancjami typu i każdy z nich musiałby mieć środki do określania równości.

Jeszcze bardziej komplikuje to, że w JavaScript rozróżnienie między danymi a metodą jest rozmyte.

Obiekt może odwoływać się do metody, która ma być wywołana jako Obsługa zdarzenia, i prawdopodobnie nie będzie to uważane za część jego "stanu wartości". Natomiast innemu obiektowi może być przypisana funkcja, która wykonuje ważną obliczenie i tym samym sprawia, że ta instancja różni się od innych po prostu dlatego, że odwołuje się do innej funkcji.

A co z obiektem, który ma jedną z istniejących metod prototypowych nadpisaną przez inną funkcję? Czy nadal można ją uznać za równą innej instancji, która w przeciwnym razie jest identyczna? Na to pytanie można odpowiedzieć tylko w każdym konkretnym przypadku dla każdego typu.

Jak wspomniano wcześniej, wyjątek byłby obiektem bez typowania. W takim wypadku jedynym sensownym wybór jest iteracyjnym i rekurencyjnym porównaniem każdego członu. Nawet wtedy trzeba zapytać, jaka jest "wartość" funkcji?

 143
Author: AnthonyWJones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-09 13:53:05

Po co odkrywać koło na nowo? Daj Lodash spróbować. Posiada szereg funkcji must-have, takich jak isEqual () .

_.isEqual(object, other);

Będzie brute force sprawdzić każdą wartość klucza - podobnie jak inne przykłady na tej stronie-za pomocą ECMAScript 5 i natywnych optymalizacji, jeśli są one dostępne w przeglądarce.

Uwaga: poprzednio ta odpowiedź zalecała podkreślenie.js, ale lodash zrobił lepszą robotę naprawiając błędy i rozwiązując problemy z konsekwencja.

 422
Author: CoolAJ86,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-25 18:40:55

Domyślny operator równości w JavaScript dla obiektów ma wartość true, gdy odnoszą się do tej samej lokalizacji w pamięci.

var x = {};
var y = {};
var z = x;

x === y; // => false
x === z; // => true

Jeśli potrzebujesz innego operatora równości, musisz dodać metodę equals(other) lub coś podobnego do niej do swoich klas, a specyfika Twojej domeny problemu określi, co dokładnie to oznacza.

Oto przykład karty do gry:

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
 138
Author: Daniel X Moore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-04 22:00:49

Jeśli pracujesz w AngularJS , Funkcja angular.equals określi, czy dwa obiekty są sobie równe. W Ember.JS Użyj isEqual.

  • angular.equals - Zobacz docs lub source aby dowiedzieć się więcej o tej metodzie. Robi też Głębokie porównanie na tablicach.
  • Ember.js isEqual - Zobacz docs lub source aby dowiedzieć się więcej na temat tej metody. Nie robi głębokiego porównania na tablicach.

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>
 68
Author: Troy Harvey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-08 03:49:58

To moja wersja. Używa nowego obiektu .keys funkcja wprowadzona w ES5 i pomysły / testy z +, + oraz +:

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 } }));
 50
Author: Ebrahim Byagowi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:38

Jeśli używasz biblioteki JSON, możesz zakodować każdy obiekt jako JSON, a następnie porównać wynikowe ciągi znaków pod kątem równości.

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

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

Uwaga: chociaż ta odpowiedź będzie działać w wielu przypadkach, jak kilka osób zauważyło w komentarzach, jest problematyczna z różnych powodów. W prawie wszystkich przypadkach będziesz chciał znaleźć bardziej solidne rozwiązanie.

 44
Author: Joel Anair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-01 13:09:40

Short functional deepEqual implementacja:

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

Edit : Wersja 2, używając sugestii jib i funkcji strzałek 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);
}
 21
Author: atmin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-05 17:49:38

Jeśli masz pod ręką funkcję głębokiego kopiowania, możesz użyć poniższej sztuczki, aby nadal użyć JSON.stringify podczas dopasowywania kolejności właściwości:

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

Demo: http://jsfiddle.net/CU3vb/3/

Uzasadnienie:

Ponieważ właściwości {[2] } są kopiowane do klonu jeden po drugim, ich kolejność w klonie zostanie zachowana. A gdy właściwości obj2 zostaną skopiowane do klonu, ponieważ właściwości już istniejące w obj1 zostaną po prostu nadpisane, ich kolejność w klonie zostaną zachowane.

 20
Author: Ates Goral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-26 15:37:21

W Węźle.js, możesz użyć jego natywnego require("assert").deepEqual. Więcej informacji: http://nodejs.org/api/assert.html

Na przykład:

var assert = require("assert");
assert.deepEqual({a:1, b:2}, {a:1, b:3}); // will throw AssertionError

Kolejny przykład, który zwraca true / false zamiast zwracać błędy:

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;
};
 16
Author: Rafael Xavier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 12:41:02

Próbujesz sprawdzić, czy dwa obiekty są sobie równe? ie: ich właściwości są równe?

Jeśli tak jest, prawdopodobnie zauważyłeś tę sytuację:

var a = { foo : "bar" };
var b = { foo : "bar" };
alert (a == b ? "Equal" : "Not equal");
// "Not equal"

Być może będziesz musiał zrobić coś takiego:

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

Oczywiście ta funkcja mogłaby przydać się z dużą optymalizacją i możliwością głębokiego sprawdzania (do obsługi zagnieżdżonych obiektów: var a = { foo : { fu : "bar" } }), ale masz pomysł.

Jeśli chodzi o zaznaczone, być może będziesz musiał dostosować to do własnych celów, np.: różne klasy mogą mieć różne definicje "równych". Jeśli pracujesz tylko z prostymi obiektami, powyższe może wystarczyć, w przeciwnym razie niestandardowa Funkcja MyClass.equals() może być drogą do zrobienia.

 15
Author: nickf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-14 13:59:48

Najprostsze i logiczne rozwiązania do porównywania wszystkiego jak Object, Array, String, Int...

JSON.stringify({a: val1}) === JSON.stringify({a: val2})

Uwaga:

  • musisz zastąpić val1i val2 swoim obiektem
  • dla obiektu, musisz sortować (według klucza) rekurencyjnie dla obu obiektów po stronie
 14
Author: Pratik Bhalodiya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-19 06:01:28

Używam tej funkcji comparable do tworzenia kopii moich obiektów, które są porównywalne z 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>

Przydaje się w testach (większość frameworków testowych posiada funkcję is). Np.

is(JSON.stringify(comparable(x)), JSON.stringify(comparable(y)), 'x must match y');

Jeśli różnica jest wychwycona, łańcuchy są rejestrowane, co czyni różnice spottable:

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}}.
 10
Author: jib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-05 13:08:26

Heres jest rozwiązaniem w ES6 / ES2015 przy użyciu podejścia w stylu funkcjonalnym:

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

Demo dostępne tutaj

 9
Author: Alan R. Soares,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-06 22:30:38

Prostym rozwiązaniem tego problemu, którego wiele osób nie zdaje sobie sprawy, jest sortowanie łańcuchów JSON (na znak). Jest to również zwykle szybsze niż inne rozwiązania wymienione tutaj:

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(''));
}

Kolejną użyteczną rzeczą w tej metodzie jest to, że można filtrować porównania, przekazując funkcję" zamiennik " do JSON.funkcje stringify (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Example_of_using_replacer_parameter). następujące porównuje tylko wszystkie klucze obiektów o nazwie"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;
});
 6
Author: th317erd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-10 18:39:37

Możesz użyć _.isEqual(obj1, obj2) z podkreślenia.biblioteka js.

Oto przykład:

var stooge = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone  = {name: 'moe', luckyNumbers: [13, 27, 34]};
stooge == clone;
=> false
_.isEqual(stooge, clone);
=> true

Zobacz oficjalną dokumentację stąd: http://underscorejs.org/#isEqual

 5
Author: Bentaiba Miled Basma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-20 18:28:18

Jeśli używasz ES6+ przez Babel lub w inny sposób, możesz również użyć Object.is(x, y).

Odniesienie: http://wiki.ecmascript.org/doku.php?id=harmony:egal#object.is_x_y

 4
Author: Daniel Li,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 20:48:40

Nie wiem, czy ktoś opublikował coś podobnego do tego, ale oto funkcja, którą zrobiłem, aby sprawdzić równania obiektów.

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

Jest również rekurencyjny, więc może również sprawdzić głęboką równość, jeśli tak to nazywasz.

 4
Author: Zac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-05 17:13:25

Odradzam hashowanie czy serializację (jak sugeruje rozwiązanie JSON). Jeśli chcesz sprawdzić, czy dwa obiekty są sobie równe, musisz zdefiniować, co oznacza equals. Może być tak, że wszystkie elementy danych w obu obiektach pasują do siebie, lub może być tak, że muszą pasować lokalizacje pamięci (co oznacza, że obie zmienne odnoszą się do tego samego obiektu w pamięci), lub może być tak, że tylko jeden element danych w każdym obiekcie musi pasować.

Ostatnio opracowałem obiekt, którego Konstruktor tworzy nowe id (zaczynając od 1 i zwiększa się o 1) za każdym razem, gdy instancja jest tworzona. Obiekt ten posiada funkcję isEqual, która porównuje wartość id z wartością id innego obiektu i zwraca true, jeśli są zgodne.

W takim przypadku zdefiniowałem "equal", co oznacza, że wartości id pasują. Biorąc pod uwagę, że każda instancja ma unikalny identyfikator, można to wykorzystać do wymuszenia idei, że pasujące obiekty zajmują również tę samą lokalizację pamięci. Chociaż nie jest to konieczne.

 3
Author: Bernard Igiri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 16:20:00

Potrzebując bardziej ogólnej funkcji porównywania obiektów niż została opublikowana, przygotowałem następujące. Krytyka doceniona...

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
Author: Liam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-27 12:00:59

Jeśli porównujesz obiekty JSON możesz użyć https://github.com/mirek/node-rus-diff

npm install rus-diff

Użycie:

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

Jeśli dwa obiekty są różne, zwracany jest podobny obiekt zgodny z MongoDB {$rename:{...}, $unset:{...}, $set:{...}}.

 3
Author: Mirek Rusin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-11 17:44:49

Stanąłem przed tym samym problemem i postanowiłem napisać własne rozwiązanie. Ale ponieważ chcę również porównywać tablice z obiektami i odwrotnie, stworzyłem ogólne rozwiązanie. Postanowiłem dodać funkcje do prototypu, ale można je łatwo przepisać na samodzielne funkcje. Oto kod:

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

Algorytm ten jest podzielony na dwie części; samą funkcję equals oraz funkcję znajdującą indeks liczbowy właściwości w tablicy / obiekcie. Funkcja find jest potrzebna tylko dlatego, że indexof znajduje tylko liczby i łańcuchy znaków i żadnych obiektów .

Można to nazwać tak:

({a: 1, b: "h"}).equals({a: 1, b: "h"});

Funkcja zwraca true lub false, w tym przypadku true. Algorytm als umożliwia porównanie bardzo złożonych obiektów:

({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"]})

Górny przykład zwróci true, nawet jeśli właściwości mają inną kolejność. Jeden mały szczegół, na który należy zwrócić uwagę: ten kod sprawdza również ten sam typ dwóch zmiennych, więc "3" nie jest tym samym co 3.

 3
Author: Sir_baaron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 07:51:08

Warto rozważyć dwa obiekty równe, jeśli mają te same wartości dla wszystkich właściwości i rekurencyjnie dla wszystkich zagnieżdżonych obiektów i tablic. Uważam również, że następujące dwa obiekty są równe:

var a = {p1: 1};
var b = {p1: 1, p2: undefined};

Podobnie tablice mogą mieć "brakujące" elementy i elementy niezdefiniowane. Ja też bym je traktował tak samo:

var c = [1, 2];
var d = [1, 2, undefined];

Funkcja implementująca tę definicję równości:

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

Kod źródłowy (w tym funkcje pomocnicze, generalType i uniqueArray): test jednostkowy i Test biegacza tutaj .

 2
Author: mckoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-28 19:52:42

Przy użyciu tej funkcji dokonuję następujących założeń:

  1. kontrolujesz obiekty, które porównujesz i masz tylko prymitywne wartości (np. nie zagnieżdżone obiekty, funkcje, itp.).
  2. twoja przeglądarka obsługuje obiekt .klucze .
To powinno być traktowane jako demonstracja prostej strategii.
/**
 * 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;
}
 2
Author: Aldo Fregoso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-27 01:39:08

Jest to dodatek do wszystkich powyższych, a nie zamiennik. Jeśli potrzebujesz szybko płytko-porównaj obiekty bez konieczności sprawdzania dodatkowych przypadków rekurencyjnych. Oto strzał.

To porównuje: 1) równość liczby własnych właściwości, 2) równość nazw kluczy, 3) if bCompareValues == true, równość odpowiednich wartości właściwości i ich typów (potrójna równość)

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;
}
 2
Author: Lex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-21 01:09:08

Do porównywania kluczy dla prostych instancji obiektu klucz / wartość używam:

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

Po porównaniu kluczy wystarczy prosta dodatkowa pętla for..in.

Złożoność jest O (N*N) Z N jest liczbą kluczy.

Mam nadzieję, że / domyślam się, że obiekty, które definiuję, nie będą posiadać więcej niż 1000 właściwości...

 2
Author: Hefeust CORTES,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-09 15:03:08

Wiem, że to trochę stare, ale chciałbym dodać rozwiązanie, które wymyśliłem dla tego problemu. Miałem obiekt i chciałem wiedzieć, kiedy jego DANE się zmieniły. "coś podobnego do obiektu.obserwuj " I to co zrobiłem było:

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

To tutaj może być zduplikowane i utworzyć inny zestaw tablic, aby porównać wartości i klucze. Jest to bardzo proste, ponieważ są teraz tablicami i zwracają false, jeśli obiekty mają różne rozmiary.

 2
Author: inoabrian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-11 20:47:58

Wyciągam z mojej osobistej biblioteki, z której wielokrotnie korzystam w pracy. Następująca funkcja jest równaniem rekurencyjnym głębokim równym, które nie sprawdza

  • równość klas
  • odziedziczone wartości
  • wartości ścisła równość

Używam tego głównie do sprawdzania, czy otrzymuję równe odpowiedzi względem różnych implementacji API. Gdzie może wystąpić różnica implementacji (np. string vs number) i dodatkowe wartości null.

Jego realizacja jest dość proste i krótkie (jeśli wszystkie komentarze zostaną usunięte)

/** 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!");
	}
})();
 2
Author: PicoCreator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-17 16:06:40

Chciałem tylko dodać moją wersję porównywania obiektów z wykorzystaniem niektórych funkcji es6. Nie bierze pod uwagę zamówienia. Po konwersji wszystkich if / else na ternary doszedłem z następującym:

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;

        }
    )
}
 2
Author: Egor Litvinchuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-15 16:02:49

Oto wersja sztuczki stringify, która jest mniej typowania i działa w wielu przypadkach dla trywialnych porównań danych 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 { ... }
 2
Author: EJW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-08 22:15:23

Jest bardzo prosta poprawka dla tego, wszystko, co musisz zrobić, to JSON.stringify() na obu obiektach podczas porównywania tych dwóch.

 2
Author: ProgramKiddo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-30 12:29:11