Operator ścisłej równości powie ci, czy dwa typy obiektów są równe. Czy istnieje jednak sposób, aby stwierdzić, czy dwa obiekty są równe, podobnie jak wartość kodu skrótu w Javie?
Pytanie o przepełnienie stosu Czy w JavaScript jest jakaś funkcja hashCode? jest podobny do tego pytania, ale wymaga bardziej akademickiej odpowiedzi. Powyższy scenariusz pokazuje, dlaczego taki miałby być konieczny, i zastanawiam się, czy istnieje jakieś równoważne rozwiązanie .
javascript
object
equals
hashcode
Społeczność
źródło
źródło
a.hashCode() == b.hashCode()
czy nie oznacza to, żea
jest równab
. Jest to warunek konieczny, niewystarczający.Odpowiedzi:
Krótka odpowiedź
Prosta odpowiedź brzmi: nie, nie ma ogólnych sposobów na ustalenie, że obiekt jest równy drugiemu w sensie, który masz na myśli. Wyjątkiem jest sytuacja, gdy myślisz ściśle o tym, że obiekt nie jest typem.
Długa odpowiedź
Chodzi o metodę Equals, która porównuje dwa różne wystąpienia obiektu, aby wskazać, czy są one równe na poziomie wartości. Jednak to od konkretnego typu zależy określenie
Equals
sposobu implementacji metody. Iteracyjne porównanie atrybutów, które mają prymitywne wartości, może nie wystarczyć, mogą istnieć atrybuty, których nie należy uważać za część wartości obiektu. Na przykład,W powyższym przypadku
c
nie jest naprawdę ważne ustalenie, czy jakieś dwa wystąpienia MyClass są równe, tylkoa
ib
są ważne. W niektórych przypadkachc
mogą się różnić między instancjami, ale nie są znaczące podczas porównania.Uwaga: ta kwestia ma zastosowanie, gdy członkowie mogą być również instancjami danego typu i każdy z nich musiałby mieć środki do ustalenia równości.
Jeszcze bardziej skomplikowane jest to, że w JavaScript rozróżnia się rozróżnienie między danymi a metodą.
Obiekt może odwoływać się do metody, która ma zostać wywołana jako moduł obsługi zdarzeń, i prawdopodobnie nie będzie to uważane za część jego „stanu wartości”. Podczas gdy do innego obiektu można również przypisać funkcję, która wykonuje ważne obliczenia, a tym samym odróżnia ten przypadek od innych tylko dlatego, że odwołuje się do innej funkcji.
Co z obiektem, który ma jedną z istniejących metod prototypowych zastąpioną przez inną funkcję? Czy nadal można uznać to za równe innej instancji, która w przeciwnym razie byłaby identyczna? Na to pytanie można odpowiedzieć tylko w każdym konkretnym przypadku dla każdego rodzaju.
Jak wspomniano wcześniej, wyjątkiem byłby obiekt całkowicie bez typu. W takim przypadku jedynym rozsądnym wyborem jest iteracyjne i rekurencyjne porównanie każdego członka. Nawet wtedy trzeba zapytać, jaka jest „wartość” funkcji?
źródło
_.isEqual(obj1, obj2);
.equals
metody nie jest trywialne, dlatego w Effective Java istnieje taki temat .javascript equality object
, dostałem odpowiedź tl; dr, wziął jeden wiersz z komentarza @chovy. dziękujęangular.equals
Po co wymyślać koło ponownie? Wypróbuj Lodash . Ma wiele niezbędnych funkcji, takich jak isEqual () .
Brutalna siła sprawdzi każdą kluczową wartość - podobnie jak inne przykłady na tej stronie - używając ECMAScript 5 i natywnych optymalizacji, jeśli są one dostępne w przeglądarce.
Uwaga: poprzednio ta odpowiedź zalecała Underscore.js , ale lodash lepiej radził sobie z naprawianiem błędów i spójnym rozwiązywaniem problemów.
źródło
Domyślny operator równości w JavaScript dla Objects daje true, gdy odnoszą się do tej samej lokalizacji w pamięci.
Jeśli potrzebujesz innego operatora równości, musisz dodać
equals(other)
metodę lub coś podobnego do swoich klas, a specyfika domeny problemu określi, co to dokładnie oznacza.Oto przykład karty do gry:
źródło
{x:1, y:2}
! =={y:2, x:1}
Jeśli pracujesz w AngularJS ,
angular.equals
funkcja określi, czy dwa obiekty są równe. W Ember.js użyjisEqual
.angular.equals
- Zobacz dokumentację lub źródło, aby uzyskać więcej informacji na temat tej metody. Dokładnie porównuje również tablice.isEqual
- więcej informacji na temat tej metody można znaleźć w dokumentacji lub źródle . Nie dokonuje głębokiego porównania tablic.źródło
To jest moja wersja. Korzysta z nowej funkcji Object.keys, która została wprowadzona w ES5, oraz pomysłów / testów z + , + i + :
źródło
objectEquals([1,2,undefined],[1,2])
zwrotytrue
objectEquals([1,2,3],{0:1,1:2,2:3})
zwraca równieżtrue
- np. nie ma sprawdzania typu, tylko sprawdzanie klucza / wartości.objectEquals(new Date(1234),1234)
zwracatrue
Jeśli używasz biblioteki JSON, możesz zakodować każdy obiekt jako JSON, a następnie porównać otrzymane ciągi pod kątem równości.
UWAGA: Chociaż odpowiedź ta zadziała w wielu przypadkach, jak zauważyło kilka osób w komentarzach, jest ona problematyczna z różnych powodów. W większości przypadków będziesz chciał znaleźć bardziej niezawodne rozwiązanie.
źródło
Krótkie
deepEqual
wdrożenie funkcjonalne :Edycja : wersja 2, używając sugestii wysięgnika i funkcji strzałek ES6:
źródło
reduce
zevery
uprościć.Object.keys(x).every(key => deepEqual(x[key], y[key]))
.: (x === y)
z: (x === y && (x != null && y != null || x.constructor === y.constructor))
Czy próbujesz sprawdzić, czy dwa obiekty są równe? tj .: ich właściwości są równe?
W takim przypadku prawdopodobnie zauważysz tę sytuację:
być może będziesz musiał zrobić coś takiego:
Oczywiście ta funkcja mogłaby przydać się z dość 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ł.Jak wskazano na FOR, być może będziesz musiał dostosować to do własnych celów, np .: różne klasy mogą mieć różne definicje „równości”. Jeśli pracujesz tylko z prostymi obiektami, powyższe może wystarczyć, w przeciwnym razie
MyClass.equals()
możesz skorzystać z funkcji niestandardowej .źródło
Jeśli masz pod ręką funkcję głębokiego kopiowania, możesz skorzystać z następującej sztuczki, aby nadal używać
JSON.stringify
, dopasowując kolejność właściwości:Demo: http://jsfiddle.net/CU3vb/3/
Racjonalne uzasadnienie:
Ponieważ właściwości
obj1
są kopiowane do klonu jeden po drugim, ich kolejność w klonie zostanie zachowana. A kiedy właściwościobj2
zostaną skopiowane do klonu, ponieważ właściwości już istniejące wobj1
zostaną po prostu nadpisane, ich zamówienia w klonie zostaną zachowane.źródło
W Node.js możesz użyć jego natywnego
require("assert").deepStrictEqual
. Więcej informacji: http://nodejs.org/api/assert.htmlNa przykład:
Kolejny przykład, który zwraca
true
/false
zamiast zwraca błędy:źródło
Chai
ma również tę funkcję. W takim przypadku użyjesz:var foo = { a: 1 }; var bar = { a: 1 }; expect(foo).to.deep.equal(bar); // true;
error.name
na"AssertionError [ERR_ASSERTION]"
. W takim przypadku zastąpiłbym instrukcję ifif (error.code === 'ERR_ASSERTION') {
.deepStrictEqual
jak pójść. Dręczyłem mózg, próbując dowiedzieć się, dlaczegostrictEqual
nie działa. Fantastyczny.Najprostsze i logiczne rozwiązania do porównywania wszystkiego jak Object, Array, String, Int ...
JSON.stringify({a: val1}) === JSON.stringify({a: val2})
Uwaga:
val1
izval2
Twoim Obiektemźródło
JSON.stringify
że zmiana kolejności alfabetycznej? (Których nie mogę znaleźć udokumentowane .)Używam tej
comparable
funkcji do tworzenia kopii moich obiektów, które są porównywalne z JSON:Przydaje się w testach (większość frameworków testowych ma swoją
is
funkcję). Na przykładW przypadku wychwycenia różnicy zapisywane są ciągi, dzięki czemu różnice są widoczne:
źródło
Oto rozwiązanie w ES6 / ES2015 wykorzystujące podejście funkcjonalne:
demo dostępne tutaj
źródło
Nie wiem, czy ktoś opublikował coś podobnego do tego, ale oto funkcja, którą sprawdziłem pod kątem równości obiektów.
Ponadto jest rekurencyjny, więc może również sprawdzać głęboką równość, jeśli tak to nazywacie.
źródło
Dla tych, którzy używają NodeJS, istnieje wygodna metoda wywoływana
isDeepStrictEqual
w natywnej bibliotece Util, która może to osiągnąć.https://nodejs.org/api/util.html#util_util_isdeepstrictequal_val1_val2
źródło
ES6: Oto minimalny kod, który mógłbym zrobić. Dokonuje głębokiego porównania rekurencyjnie poprzez stritowanie wszystkich obiektów, jedynym ograniczeniem jest brak metod lub porównywanie symboli.
źródło
możesz użyć
_.isEqual(obj1, obj2)
z biblioteki underscore.js.Oto przykład:
Zobacz oficjalną dokumentację tutaj: http://underscorejs.org/#isEqual
źródło
Zakładając, że kolejność właściwości w obiekcie nie ulega zmianie.
JSON.stringify () działa na głębokie i nie głębokie oba typy obiektów, niezbyt pewny aspektów wydajnościowych:
źródło
Prostym rozwiązaniem tego problemu, którego wiele osób nie zdaje sobie sprawy, jest sortowanie ciągów JSON (według znaków). Jest to również zwykle szybsze niż w przypadku innych wymienionych tutaj rozwiązań:
Kolejną przydatną rzeczą w tej metodzie jest to, że możesz filtrować porównania, przekazując funkcję „zamiennika” do funkcji JSON.stringify ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON / stringify # Example_of_using_replacer_parameter ). Poniższe porównuje tylko wszystkie klucze obiektów o nazwie „derp”:
źródło
areEqual({a: 'b'}, {b: 'a'})
dostajetrue
to?Chciałem tylko dodać moją wersję porównania obiektów wykorzystującą niektóre funkcje es6. Nie uwzględnia zamówienia. Po konwersji wszystkich if / else na trójkę przyszedłem z następującymi elementami:
źródło
Potrzebując bardziej ogólnej funkcji porównywania obiektów niż wcześniej napisałem, przygotowałem następujące. Doceniona krytyka ...
źródło
Object.prototype
- w zdecydowanej większości przypadków nie jest to zalecane (dodatki pojawiają się na przykład we wszystkich pętlach for..in). Może rozważyćObject.equals = function(aObj, bObj) {...}
?Jeśli porównujesz obiekty JSON, możesz użyć https://github.com/mirek/node-rus-diff
Stosowanie:
Jeśli dwa obiekty są różne,
{$rename:{...}, $unset:{...}, $set:{...}}
zwracany jest obiekt podobny do MongoDB .źródło
Napotkałem ten sam problem 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 łatwo przepisać je do samodzielnych funkcji. Oto kod:
Ten algorytm jest podzielony na dwie części; Sama funkcja równa się i funkcja znajdująca indeks numeryczny właściwości w tablicy / obiekcie. Funkcja wyszukiwania jest potrzebna tylko dlatego, że indexof znajduje tylko liczby i ciągi znaków, a nie żadnych obiektów.
Można to tak nazwać:
Funkcja zwraca true lub false, w tym przypadku true. Algorytm pozwala również na porównanie bardzo złożonych obiektów:
Górny przykład zwróci wartość 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” to nie to samo co 3.
źródło
Widzę odpowiedzi na kod spaghetti. Bez korzystania z bibliotek stron trzecich jest to bardzo łatwe.
Najpierw posortuj dwa obiekty według klucza ich nazw kluczy.
Następnie po prostu użyj ciągu znaków, aby je porównać.
źródło
Odradzam mieszanie lub serializację (jak sugerują rozwiązania JSON). Jeśli musisz sprawdzić, czy dwa obiekty są równe, musisz zdefiniować, co oznacza równość. Może się zdarzyć, że wszystkie elementy danych w obu obiektach będą pasować, lub może być tak, że lokalizacje pamięci muszą się zgadzać (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 nowy identyfikator (zaczynając od 1 i zwiększając o 1) za każdym razem, gdy tworzona jest instancja. Ten obiekt ma funkcję isEqual, która porównuje tę wartość id z wartością id innego obiektu i zwraca wartość true, jeśli są one zgodne.
W takim przypadku zdefiniowałem „równy” jako oznaczający, że wartości id są zgodne. Biorąc pod uwagę, że każda instancja ma unikalny identyfikator, można to wykorzystać do wymuszenia przekonania, że pasujące obiekty również zajmują to samo miejsce w pamięci. Chociaż nie jest to konieczne.
źródło
Przydatne jest uznanie dwóch obiektów za równe, jeśli mają wszystkie takie same wartości dla wszystkich właściwości i rekurencyjnie dla wszystkich zagnieżdżonych obiektów i tablic. Uważam również następujące dwa obiekty za równe:
Podobnie tablice mogą mieć „brakujące” elementy i niezdefiniowane elementy. Traktowałbym je również tak samo:
Funkcja, która implementuje tę definicję równości:
Kod źródłowy (w tym funkcje pomocnicze, generalType i uniqueArray): Test jednostkowy i Test Runner tutaj .
źródło
Za pomocą tej funkcji przyjmuję następujące założenia:
Należy to potraktować jako demonstrację prostej strategii.
źródło
Jest to dodatek do wszystkich powyższych, a nie zamiennik. Jeśli chcesz szybko porównać obiekty bez konieczności sprawdzania dodatkowych przypadków rekurencyjnych. Oto strzał.
Dla porównania: 1) Równość liczby własnych właściwości, 2) Równość nazw kluczy, 3) jeśli bCompareValues == true, Równość odpowiadających wartości właściwości i ich typów (potrójna równość)
źródło
Do porównywania kluczy dla instancji obiektów prostych par klucz / wartość używam:
Po porównaniu kluczy wystarczy zwykła dodatkowa
for..in
pętla.Złożoność to O (N * N), a N to liczba kluczy.
Mam nadzieję, że obiekty, które zdefiniuję, nie będą zawierać więcej niż 1000 właściwości ...
źródło
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 zmieniły się jego dane. „coś podobnego do Object.observe” i to, co zrobiłem, to:
Można to tutaj powielić i stworzyć inny zestaw tablic, aby porównać wartości i klucze. Jest to bardzo proste, ponieważ są teraz tablicami i zwrócą wartość false, jeśli obiekty mają różne rozmiary.
źródło
false
, ponieważ tablice nie porównują wartości, np[1,2] != [1,2]
.