Jak porównać tablice w JavaScript?


988

Chciałbym porównać dwie tablice ... idealnie, efektywnie. Nic szczególnego, tylko truejeśli są identyczne, a falsejeśli nie. Nic dziwnego, że operator porównania nie działa.

var a1 = [1,2,3];
var a2 = [1,2,3];
console.log(a1==a2);    // Returns false
console.log(JSON.stringify(a1)==JSON.stringify(a2));    // Returns true

JSON koduje każdą tablicę, ale czy istnieje szybszy lub „lepszy” sposób na proste porównywanie tablic bez konieczności iterowania każdej wartości?


5
Możesz najpierw porównać ich długość, a jeśli są one równe, każda wartość.
TJHeuvel

55
Co sprawia, że ​​dwie tablice są dla ciebie równe? Te same elementy? Ta sama kolejność elementów? Kodowanie jako JSON działa tylko tak długo, jak element tablicy może być szeregowany do JSON. Jeśli tablica może zawierać obiekty, jak głęboko sięgnąłbyś? Kiedy dwa obiekty są „równe”?
Felix Kling

48
@FelixKling, zdefiniowanie „równości” jest zdecydowanie subtelnym tematem, ale dla osób przychodzących do JavaScript z języków wyższego poziomu nie ma usprawiedliwienia dla takich głupot ([] == []) == false.
Alex D

4
@AlexD wygląda na to, że tablice używają równości odniesienia, czego się można spodziewać. Byłoby okropnie, gdybyś nie mógł tego zrobić
JonnyRaa

3
@AlexD Nie potrafię wymyślić języka, w którym tak się nie dzieje. W C ++ porównywałbyś dwa wskaźniki - false. W Javie robisz to samo, co w javascript. W PHP coś za kulisami przechodzi przez tablice - czy nazywasz PHP językiem wyższego poziomu?
Tomáš Zato - Przywróć Monikę

Odpowiedzi:


877

Aby porównać tablice, przejrzyj je i porównaj każdą wartość:

Porównywanie tablic:

// Warn if overriding existing method
if(Array.prototype.equals)
    console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

Stosowanie:

[1, 2, [3, 4]].equals([1, 2, [3, 2]]) === false;
[1, "2,3"].equals([1, 2, 3]) === false;
[1, 2, [3, 4]].equals([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].equals([1, 2, 1, 2]) === true;

Możesz powiedzieć „ Ale porównywanie ciągów jest znacznie szybsze - bez pętli ... ”, więc powinieneś zauważyć, że istnieją pętle ARE. Pierwsza pętla rekurencyjna, która konwertuje Array na łańcuch, a druga, która porównuje dwa łańcuchy. Tak więc ta metoda jest szybsza niż użycie ciągu .

Uważam, że większe ilości danych powinny zawsze być przechowywane w tablicach, a nie w obiektach. Jeśli jednak używasz obiektów, można je również częściowo porównać.
Oto jak:

Porównywanie obiektów:

Powiedziałem powyżej, że dwa wystąpienia obiektów nigdy nie będą równe, nawet jeśli w tej chwili zawierają te same dane:

({a:1, foo:"bar", numberOfTheBeast: 666}) == ({a:1, foo:"bar", numberOfTheBeast: 666})  //false

Ma to powód, ponieważ w obiektach mogą znajdować się na przykład zmienne prywatne.

Jeśli jednak użyjesz struktury obiektu do przechowywania danych, porównanie jest nadal możliwe:

Object.prototype.equals = function(object2) {
    //For the first loop, we only check for types
    for (propName in this) {
        //Check for inherited methods and properties - like .equals itself
        //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
        //Return false if the return value is different
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        //Check instance type
        else if (typeof this[propName] != typeof object2[propName]) {
            //Different types => not equal
            return false;
        }
    }
    //Now a deeper check using other objects property names
    for(propName in object2) {
        //We must check instances anyway, there may be a property that only exists in object2
            //I wonder, if remembering the checked values from the first loop would be faster or not 
        if (this.hasOwnProperty(propName) != object2.hasOwnProperty(propName)) {
            return false;
        }
        else if (typeof this[propName] != typeof object2[propName]) {
            return false;
        }
        //If the property is inherited, do not check any more (it must be equa if both objects inherit it)
        if(!this.hasOwnProperty(propName))
          continue;

        //Now the detail check and recursion

        //This returns the script back to the array comparing
        /**REQUIRES Array.equals**/
        if (this[propName] instanceof Array && object2[propName] instanceof Array) {
                   // recurse into the nested arrays
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        else if (this[propName] instanceof Object && object2[propName] instanceof Object) {
                   // recurse into another objects
                   //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
           if (!this[propName].equals(object2[propName]))
                        return false;
        }
        //Normal value comparison for strings and numbers
        else if(this[propName] != object2[propName]) {
           return false;
        }
    }
    //If everything passed, let's say YES
    return true;
}  

Pamiętaj jednak, że ten ma służyć do porównywania danych podobnych do JSON, a nie instancji klas i innych rzeczy. Jeśli chcesz porównać bardziej skomplikowane obiekty, spójrz na tę odpowiedź i jej bardzo długą funkcję .
Aby to działało Array.equals, musisz nieco zmodyfikować oryginalną funkcję:

...
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
        // recurse into the nested arrays
        if (!this[i].equals(array[i]))
            return false;
    }
    /**REQUIRES OBJECT COMPARE**/
    else if (this[i] instanceof Object && array[i] instanceof Object) {
        // recurse into another objects
        //console.log("Recursing to compare ", this[propName],"with",object2[propName], " both named \""+propName+"\"");
        if (!this[i].equals(array[i]))
            return false;
        }
    else if (this[i] != array[i]) {
...

Zrobiłem małe narzędzie testowe dla obu funkcji .

Bonus: tablice zagnieżdżone z indexOficontains

Samy Bencherif przygotował przydatne funkcje na wypadek, gdy szukasz określonego obiektu w zagnieżdżonych tablicach, które są dostępne tutaj: https://jsfiddle.net/SamyBencherif/8352y6yw/


27
Jeśli chcesz dokonać dokładnych porównań, użyj this[i] !== array[i]zamiast !=.
Tim S.

38
Metoda powinna być wywoływana equalszamiast compare. Przynajmniej w .NET porównanie zwykle zwraca podpisany znak int wskazujący, który obiekt jest większy od drugiego. Zobacz: Comparer.Compare .
Oliver

15
Nie tylko jest to właściwy sposób, ale także znacznie bardziej wydajny. Oto szybki skrypt jsperf, który przygotowałem dla wszystkich metod sugerowanych w tym pytaniu. jsperf.com/comparing-arrays2
Tolga E

96
Zmiana prototypu typu wbudowanego zdecydowanie nie jest właściwa
Jasper

31
Poza tym nie chodzi o to, czy łatwo przepisać, chodzi o to, że odpowiedź nie powinna zalecać czegoś, co jest uważane za złą praktykę ( developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/... ) i powinno zdecydowanie nie rób tego pod nagłówkiem „Właściwa droga”
Jasper

386

Chociaż działa to tylko w przypadku tablic skalarnych (patrz uwaga poniżej), jest krótkie:

array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})

Rr, w ECMAScript 6 / CoffeeScript / TypeScript z funkcjami strzałek:

array1.length === array2.length && array1.every((value, index) => value === array2[index])

(Uwaga: „skalar” oznacza tutaj wartości, które można bezpośrednio porównać ===. Tak więc: liczby, ciągi znaków, obiekty według odwołania, funkcje według odwołania. Zobacz odniesienie MDN, aby uzyskać więcej informacji na temat operatorów porównania).

AKTUALIZACJA

Z tego, co przeczytałem w komentarzach, sortowanie tablicy i porównywanie może dać dokładny wynik:

array1.length === array2.length && array1.sort().every(function(value, index) { return value === array2.sort()[index]});

Na przykład:

array1 = [2,3,1,4];
array2 = [1,2,3,4];

Wtedy powyższy kod dałby true


19
Podoba mi się to, chociaż czytelnicy powinni wiedzieć, że działa to tylko na posortowanych tablicach.
Ellen Spertus,

13
Działa na wszelkiego rodzaju tablicach, posortowanych lub nie @espertus
Michał Miszczyszyn

36
Tak, dokładnie. Ta funkcja ma porównywać dwie tablice, nie ma znaczenia, czy są one posortowane, czy nie, ich kolejne elementy muszą być równe.
Michał Miszczyszyn

22
@espertus Rzeczywiście, nie zwróci prawdy, jeśli elementy nie będą miały dokładnie tej samej kolejności w obu tablicach. Jednak celem kontroli równości nie jest sprawdzenie, czy zawierają te same elementy, ale sprawdzenie, czy mają ten sam element w tych samych zamówieniach.
Quentin Roy

7
Jeśli chcesz sprawdzić, czy obie tablice są równe, zawierające te same nieposortowane elementy (ale nieużywane wiele razy), możesz użyć a1.length==a2.length && a1.every((v,i)=>a2.includes(v)): var a1 =[1,2,3], a2 = [3,2,1];( var a1 =[1,3,3], a2 = [1,1,3];nie będzie działać zgodnie z oczekiwaniami)
mems

208

Lubię używać biblioteki Underscore do projektów kodowania tablic / obiektów ... w Underscore i Lodash, niezależnie od tego, czy porównujesz tablice czy obiekty, tak to wygląda:

_.isEqual(array1, array2)   // returns a boolean
_.isEqual(object1, object2) // returns a boolean

22
Pamiętaj, że zamówienie ma znaczenie _.isEqual([1,2,3], [2,1,3]) => false
Vitaliy Alekask

3
lub jeśli chcesz tylko isEqualfunkcjonalność, zawsze możesz użyć modułu
lodash.isequal

6
Możesz użyć _.difference (); jeśli zamówienie nie ma dla ciebie znaczenia
Ronan Quillevere

5
Możemy posortować tablicę przed tym sprawdzeniem, jeśli kolejność nie ma znaczenia_.isEqual([1,2,3].sort(), [2,1,3].sort()) => true
Filype

najbardziej zwięzła i prosta odpowiedź IMHO :-)
Kieran Ryan

121

Myślę, że jest to najprostszy sposób, aby to zrobić za pomocą JSON stringify, i może być najlepszym rozwiązaniem w niektórych sytuacjach:

JSON.stringify(a1) === JSON.stringify(a2);

Konwertuje to obiekty a1i a2ciągi, aby można je było porównać. Kolejność jest ważna w większości przypadków, ponieważ umożliwia sortowanie obiektu za pomocą algorytmu sortowania pokazanego w jednej z powyższych odpowiedzi.

Pamiętaj, że nie porównujesz już obiektu, ale reprezentację ciągu obiektu. To może nie być dokładnie to, czego chcesz.


dobra odpowiedź, ale dlaczego [] == [] zwraca false? oba są prostymi przedmiotami, więc dlaczego?
Pardeep Jain

4
@ PardeepJain, dzieje się tak, ponieważ domyślnie operator równości w ECMAScript for Objects zwraca wartość true, gdy odnoszą się do tej samej lokalizacji pamięci. Wypróbuj var x = y = []; // teraz równość zwraca wartość true.
radtek

7
należy zauważyć, że funkcja strunowania JSON nie jest szybka. Używana z większymi tablicami z pewnością wprowadzi lag.
Lukas Liesis

6
Pytanie dotyczy w szczególności tego, czy istnieje lepszy / szybszy sposób niż użycie JSON.stringify.
Don Hatch,

Bardziej szczegółowo wyjaśnia, dlaczego może to być dobre rozwiązanie w niektórych sytuacjach.
radtek

61

Nie jest jasne, co rozumiesz przez „identyczny”. Na przykład, czy tablice ai bponiżej są identyczne (zwróć uwagę na tablice zagnieżdżone)?

var a = ["foo", ["bar"]], b = ["foo", ["bar"]];

Oto zoptymalizowana funkcja porównywania tablic, która porównuje kolejno odpowiednie elementy każdej tablicy przy użyciu ścisłej równości i nie dokonuje rekurencyjnego porównywania elementów tablicy, które same są tablicami, co oznacza, że ​​w powyższym przykładzie arraysIdentical(a, b)powróciłoby false. Działa w ogólnym przypadku, którego join()rozwiązania JSON i oparte na nim nie będą:

function arraysIdentical(a, b) {
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};

@ASDF: Pytanie, co oznacza „identyczny”, nie jest jasne. Oczywiście ta odpowiedź po prostu wykonuje płytką kontrolę. Dodam notatkę.
Tim Down

nie powiedzie się to dla tablicIdentical ([1, 2, [3, 2]], [1, 2, [3, 2]]);
Gopinath Shiva

4
@GopinathShiva: Cóż, kończy się niepowodzeniem tylko wtedy, gdy spodziewasz się, że powróci true. Odpowiedź wyjaśnia, że ​​tak nie będzie. Jeśli chcesz porównać zagnieżdżone tablice, możesz łatwo dodać kontrolę rekurencyjną.
Tim Down

59

Praktyczny sposób

Myślę, że błędem jest twierdzenie, że konkretna implementacja to „Właściwy sposób”, jeśli jest tylko „właściwa” („poprawna”) w przeciwieństwie do „złego” rozwiązania. Rozwiązanie Tomáša stanowi wyraźną poprawę w porównaniu z porównywaniem tablic łańcuchowych, ale to nie znaczy, że jest obiektywnie „właściwe”. Co właściwie jest właściwe ? Czy to jest najszybsze? Czy to jest najbardziej elastyczne? Czy najłatwiej to zrozumieć? Czy debugowanie jest najszybsze? Czy używa najmniej operacji? Czy ma jakieś skutki uboczne? Żadne rozwiązanie nie może mieć wszystkiego najlepszego.

Tomáš mógł powiedzieć, że jego rozwiązanie jest szybkie, ale powiedziałbym również, że jest to niepotrzebnie skomplikowane. Stara się być rozwiązaniem typu „wszystko w jednym”, które działa dla wszystkich tablic, zagnieżdżonych lub nie. W rzeczywistości przyjmuje nawet więcej niż tylko tablice jako dane wejściowe i nadal próbuje udzielić „prawidłowej” odpowiedzi.


Produkty generyczne oferują możliwość ponownego użycia

Moja odpowiedź podejdzie do problemu inaczej. Zacznę od ogólnej arrayCompareprocedury, która dotyczy tylko przechodzenia przez tablice. Następnie zbudujemy nasze inne podstawowe funkcje porównania, takie jak arrayEquali arrayDeepEqualitp

// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
  x === undefined && y === undefined
    ? true
    : Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)

Moim zdaniem najlepszy rodzaj kodu nie wymaga nawet komentarzy i nie jest to wyjątkiem. Tutaj dzieje się tak mało, że można zrozumieć zachowanie tej procedury bez żadnego wysiłku. Pewnie, niektóre ze składni ES6 mogą wydawać się wam teraz obce, ale tylko dlatego, że ES6 jest stosunkowo nowy.

Jak sugeruje typ, arrayCompareprzyjmuje funkcję porównania foraz dwie tablice wejściowe xsi ys. W większości przypadków wywołujemy f (x) (y)każdy element w tablicach wejściowych. Wracamy wcześnie, falsejeśli fzwrot zdefiniowany przez użytkownika false- dzięki &&ocenie zwarcia. Tak, oznacza to, że komparator może wcześniej przerwać iterację i zapobiec zapętlaniu się przez resztę tablicy wejściowej, gdy nie jest to konieczne.


Ścisłe porównanie

Następnie, korzystając z naszej arrayComparefunkcji, możemy łatwo stworzyć inne funkcje, których możemy potrzebować. Zaczniemy od elementarnego arrayEqual

// equal :: a -> a -> Bool
const equal = x => y =>
  x === y // notice: triple equal

// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
  arrayCompare (equal)

const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys))      //=> true
// (1 === 1) && (2 === 2) && (3 === 3)  //=> true

const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs))      //=> false
// (1 === '1')                          //=> false

Proste. arrayEqualmożna zdefiniować za arrayComparepomocą funkcji porównawczej, która porównuje się ado bużycia ===(dla ścisłej równości).

Zauważ, że my również definiujemy equaljako własną funkcję. Podkreśla to rolę arrayComparefunkcji wyższego rzędu w wykorzystaniu naszego komparatora pierwszego rzędu w kontekście innego typu danych (Array).


Luźne porównanie

Możemy równie łatwo zdefiniować arrayLooseEqualza pomocą ==zamiast. Teraz podczas porównywania 1(Liczba) do '1'(Ciąg) wynikiem będzie true

// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
  x == y // notice: double equal

// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
  arrayCompare (looseEqual)

const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys))    //=> true
// (1 == '1') && (2 == '2') && (3 == '3')  //=> true

Głębokie porównanie (rekurencyjne)

Prawdopodobnie zauważyłeś, że jest to tylko płytkie porównanie. Z pewnością rozwiązaniem Tomáša jest „The Right Way ™”, ponieważ zawiera niejawne głębokie porównanie, prawda?

Nasza arrayCompareprocedura jest na tyle wszechstronna, że ​​można ją stosować w taki sposób, że test głębokiej równości jest dziecinnie prosty…

// isArray :: a -> Bool
const isArray =
  Array.isArray

// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
  arrayCompare (a => b =>
    isArray (a) && isArray (b)
      ? arrayDeepCompare (f) (a) (b)
      : f (a) (b))

const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3')         //=> false

console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3')                 //=> true

Proste. Budujemy głęboki komparator za pomocą innej funkcji wyższego rzędu. Tym razem mamy do pakowania arrayCompareprzy użyciu niestandardowych porównanie, że sprawdzi, czy ai bsą tablice. Jeśli tak, zastosuj ponownie w arrayDeepCompareinnym przypadku ai bdo komparatora określonego przez użytkownika ( f). To pozwala nam oddzielić zachowanie do głębokiego porównania od tego, jak faktycznie porównujemy poszczególne elementy. Czyli jak pokazuje powyższy przykład, możemy porównać stosując głęboko equal, looseEquallub dowolny inny komparator robimy.

Ponieważ arrayDeepComparejest curry, możemy go częściowo zastosować, tak jak w poprzednich przykładach

// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
  arrayDeepCompare (equal)

// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
  arrayDeepCompare (looseEqual)

Dla mnie jest to już wyraźna poprawa w stosunku do rozwiązania Tomáša, ponieważ w razie potrzeby mogę wyraźnie wybrać płytkie lub głębokie porównanie moich zestawów.


Porównanie obiektów (przykład)

Co teraz, jeśli masz tablicę obiektów lub coś takiego? Może chcesz uznać te tablice za „równe”, jeśli każdy obiekt ma tę samą idwartość…

// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
  x.id !== undefined && x.id === y.id

// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
  arrayCompare (idEqual)

const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2)            //=> true

const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6)            //=> false

Proste. Tutaj użyłem waniliowych obiektów JS, ale ten typ komparatora może działać dla dowolnego typu obiektu; nawet twoje niestandardowe obiekty. Rozwiązanie Tomáša musiałoby zostać całkowicie przerobione, aby obsługiwać ten rodzaj testu równości

Głęboka tablica z obiektami? Żaden problem. Zbudowaliśmy bardzo wszechstronne, ogólne funkcje, dzięki czemu będą działać w szerokim zakresie zastosowań.

const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys))     //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true

Arbitralne porównanie (przykład)

A co, jeśli chcesz dokonać innego rodzaju całkowicie arbitralnego porównania? Może chcę wiedzieć, czy każdy xjest większy niż każdy y

// gt :: Number -> Number -> Bool
const gt = x => y =>
  x > y

// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)

const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys))     //=> true
// (5 > 2) && (10 > 4) && (20 > 8)  //=> true

const zs = [6,12,24]
console.log (arrayGt (xs) (zs))     //=> false
// (5 > 6)                          //=> false

Mniej znaczy więcej

Widać, że faktycznie robimy więcej przy mniejszym kodzie. Nie ma w sobie nic skomplikowanego arrayCompare, a każdy stworzony przez nas niestandardowy komparator ma bardzo prostą implementację.

Z łatwością możemy dokładnie określić, w jaki sposób chcemy na dwie tablice należy porównać - płytkie, głębokie, ścisłe, luźne, niektóre właściwości obiektu, lub dowolna część obliczeń, lub dowolna kombinacja tych form - wszystko za pomocą jednej procedury , arrayCompare. Może nawet wymarzysz RegExpkomparator! Wiem, jak dzieci uwielbiają te wyrażenia regularne…

Czy to jest najszybsze? Nie. Ale prawdopodobnie nie musi tak być. Jeśli szybkość jest jedyną miarą używaną do pomiaru jakości naszego kodu, wiele naprawdę świetnych kodów zostałoby wyrzuconych - dlatego nazywam to podejście Praktycznym . Lub może być bardziej sprawiedliwy, praktyczny sposób. Ten opis jest odpowiedni dla tej odpowiedzi, ponieważ nie twierdzę, że ta odpowiedź jest praktyczna w porównaniu z innymi odpowiedziami; jest to obiektywnie prawdziwe. Osiągnęliśmy wysoki stopień praktyczności przy bardzo małym kodzie, o którym bardzo łatwo jest myśleć. Żaden inny kod nie może powiedzieć, że nie zasłużyliśmy na ten opis.

Czy to sprawia, że ​​jest to „właściwe” rozwiązanie dla Ciebie? To zależy od ciebie . I nikt inny nie może tego dla ciebie zrobić; tylko ty wiesz, jakie są twoje potrzeby. W prawie wszystkich przypadkach cenię prosty, praktyczny i wszechstronny kod nad sprytnym i szybkim rodzajem. To, co cenisz, może się różnić, więc wybierz to, co Ci odpowiada.


Edytować

Moja stara odpowiedź bardziej koncentrowała się na rozkładaniu arrayEqualna małe procedury. To ciekawe ćwiczenie, ale nie jest to najlepszy (najbardziej praktyczny) sposób podejścia do tego problemu. Jeśli jesteś zainteresowany, możesz zobaczyć tę historię zmian.


8
„najlepszy rodzaj kodu nie potrzebuje nawet komentarzy” ... nie chcę tego mówić, ale ten kod mógłby użyć więcej komentarza i / lub innej nazwy - „porównaj” jest dość niejasne. Jeśli czytam poprawnie, twoje „porównanie” jest w zasadzie curry rekurencyjnym „co”. Myślę. A może to curry rekurencyjne „trochę”? Hmm Wymaga to więcej myślenia niż to konieczne. Być może lepszą nazwą byłoby „arraysEquivalent”, wykorzystujące standardową terminologię „relacji równoważności”. Lub nawet jaśniej (dla mnie i tak) „rekurencyjnie równoważne”.
Don Hatch

1
@DonHatch dziękuję za możliwość odpowiedzi. Mówiąc „porównaj” masz na myśli arrayCompare? Tak, funkcja jest curry, ale różni się od somei every. arrayCompareporównuje komparator i dwie tablice. Wybrałem specyficzną nazwę ogólną, ponieważ możemy porównywać tablice za pomocą dowolnej dowolnej funkcji. Funkcja jest curry, więc można ją wyspecjalizować w tworzeniu nowych funkcji porównywania tablic (np arrayEqual.). Czy możesz zaproponować lepsze imię? Jakie obszary Twoim zdaniem wymagają dodatkowych komentarzy lub wyjaśnień? Z przyjemnością omawiam ^ _ ^
Dziękuję

1
Nie jestem pewien, czy mój punkt jest jeszcze jasny - ale chodzi mi o to, że twoja funkcja nie jest tak naprawdę przeznaczona do przyjmowania dowolnej funkcji, nie sądzę - ma ona przyjmować relację równoważności i zwraca relację równoważności. To ważne - nie zrobiłoby nic sensownego (nie sądzę), gdyby otrzymał jakąś inną dowolną funkcję binarną, taką jak te, o których wspomniałem, nawet te, które ludzie często nazywają „porównuj”. Sądzę więc, że warto byłoby wpisać „ekwiwalent” w nazwie zamiast „porównaj”.
Don Hatch,

1
@ftor, autor: super pomocna odpowiedź, dobra robota, +1. Informacja zwrotna: popierasz prostotę, ale dla wielu programistów wyrażenie z trzema strzałkami w jednej linii nie jest proste ani łatwe do zrozumienia. Na przykład: f => ([x, ... xs]) => ([y, ... ys]) =>. Ciągle tego używam i nadal musiałem to rozkładać mentalnie, zamiast „po prostu na to spojrzeć”. Drugi punkt ftor ma rację, użyj każdego. Nawet rozważając swoje powody, wydaje mi się, że lepiej nie tylko mi, ale także z twojej perspektywy, gdy próbujesz wywnioskować swoją filozofię projektowania.
Whitneyland

1
Rozumiem, że jest to miejsce do nauki, ale zakładam tutaj, że przeciętny programista studiujący styl funkcjonalny może przekształcić dowolną funkcję curry w funkcję niezabudowaną. Moja odpowiedź nie sugeruje, że ten styl powinien być używany we własnym programie - napisz go bez pośpiechu, napisz go według własnych reguł wcięcia, napisz, jak chcesz - piszę moje odpowiedzi w stylu, który moim zdaniem wyraża najlepiej programować. Chciałbym również zaprosić innych do zakwestionowania sposobu, w jaki wyrażamy nasze programy składniowo
Dziękuję

54

W duchu pierwotnego pytania:

Chciałbym porównać dwie tablice ... idealnie, efektywnie . Nic szczególnego , tylko prawda, jeśli są identyczne, i fałsz, jeśli nie.

Testy wydajności przeprowadziłem na niektórych zaproponowanych tutaj prostszych sugestiach z następującymi wynikami (od szybkiego do wolnego):

natomiast (67%) przez Tim Down

var i = a1.length;
while (i--) {
    if (a1[i] !== a2[i]) return false;
}
return true

co (69%) według użytkownika2782196

a1.every((v,i)=> v === a2[i]);

zmniejszyć (74%) o DEI

a1.reduce((a, b) => a && a2.includes(b), true);

dołączyć i toString (78%) przez Gaizka Allende i vivek

a1.join('') === a2.join('');

a1.toString() === a2.toString();

połowa do String (90%) autorstwa Victora Palomo

a1 == a2.toString();

stringify (100%) przez radtek

JSON.stringify(a1) === JSON.stringify(a2);

Zwróć uwagę, że poniższe przykłady zakładają, że tablice są sortowanymi, jednowymiarowymi tablicami. .lengthporównanie zostało usunięte dla wspólnego testu porównawczego (dodaj a1.length === a2.lengthdo dowolnej sugestii, a otrzymasz wzrost wydajności o około 10%). Wybierz rozwiązania, które najbardziej Ci odpowiadają, znając szybkość i ograniczenia każdego z nich.

Niepowiązana uwaga: interesujące jest to, że ludzie zaczynają słuchać Johna Waynesa z radością z wyzwalacza po naciśnięciu przycisku „głosuj w dół” przy całkowicie uzasadnionych odpowiedziach na to pytanie.


Link otwiera pusty test.
Alexander Abakumov

Jeśli podbijesz rozmiar tablicy, liczby te nie mają zastosowania (szczególnie podejście zmniejszania). Spróbuj z Array.from({length: 1000}).map((a,v)=> $ {v}.padStart(10,2));
Narayon

działa tylko w przypadku płytkiej tablicy
Ramesh Rajendran

28

Opierając się na odpowiedzi Tomáša Zato, zgadzam się, że iteracja po tablicach jest najszybsza. Dodatkowo (jak już inni powiedzieli), funkcję należy nazwać równą / równą, a nie porównywać. W związku z tym zmodyfikowałem tę funkcję, aby obsługiwała porównywanie tablic dla podobieństwa - tj. Mają te same elementy, ale są nieczynne - do użytku osobistego i pomyślałem, że wrzucę to tutaj, aby wszyscy mogli to zobaczyć.

Array.prototype.equals = function (array, strict) {
    if (!array)
        return false;

    if (arguments.length == 1)
        strict = true;

    if (this.length != array.length)
        return false;

    for (var i = 0; i < this.length; i++) {
        if (this[i] instanceof Array && array[i] instanceof Array) {
            if (!this[i].equals(array[i], strict))
                return false;
        }
        else if (strict && this[i] != array[i]) {
            return false;
        }
        else if (!strict) {
            return this.sort().equals(array.sort(), true);
        }
    }
    return true;
}

Ta funkcja przyjmuje dodatkowy parametr strict, który domyślnie ma wartość true. Ten ścisły parametr określa, czy tablice muszą być całkowicie równe zarówno pod względem zawartości, jak i kolejności tych treści, czy po prostu zawierają tę samą zawartość.

Przykład:

var arr1 = [1, 2, 3, 4];
var arr2 = [2, 1, 4, 3];  // Loosely equal to 1
var arr3 = [2, 2, 3, 4];  // Not equal to 1
var arr4 = [1, 2, 3, 4];  // Strictly equal to 1

arr1.equals(arr2);         // false
arr1.equals(arr2, false);  // true
arr1.equals(arr3);         // false
arr1.equals(arr3, false);  // false
arr1.equals(arr4);         // true
arr1.equals(arr4, false);  // true

Napisałem również szybkie jsfiddle z funkcją i tym przykładem:
http://jsfiddle.net/Roundaround/DLkxX/


12

Mimo że ma wiele odpowiedzi, które, jak sądzę, są pomocne:

const newArray = [ ...new Set( [...arr1, ...arr2] ) ]

Pytanie nie mówi, jak będzie wyglądać struktura tablicy, więc jeśli wiesz na pewno, że nie będziesz mieć zagnieżdżonych tablic ani obiektów w tablicy (zdarzyło mi się, dlatego do tego doszedłem odpowiedź) powyższy kod będzie działał.

Co się dzieje, używamy operatora spreadu (...) do łączenia obu tablic, a następnie używamy Setu, aby wyeliminować wszelkie duplikaty. Gdy już to zrobisz, możesz porównać ich rozmiary, jeśli wszystkie trzy tablice mają taki sam rozmiar, możesz przejść.

Ta odpowiedź ignoruje również kolejność elementów , jak już powiedziałem, przydarzyła mi się dokładna sytuacja, więc może ktoś w tej samej sytuacji może skończyć tutaj (tak jak ja).


Edytuj1.

Odpowiadając na pytanie Dmitrija Grinko: „Dlaczego użyłeś tutaj operatora rozprzestrzeniania (...) - ... nowego zestawu? To nie działa”

Rozważ ten kod:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

Dostaniesz

[ Set { 'a', 'b', 'c' } ]

Aby pracować z tą wartością, musisz użyć niektórych właściwości Set (patrz https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set ). Z drugiej strony, gdy używasz tego kodu:

const arr1 = [ 'a', 'b' ]
const arr2 = [ 'a', 'b', 'c' ]
const newArray = [ ...new Set( [...arr1, ...arr2] ) ]
console.log(newArray)

Dostaniesz

[ 'a', 'b', 'c' ]

To różnica, ta pierwsza dałaby mi Set, to też działałoby, ponieważ mogłem uzyskać rozmiar tego Seta, ale druga daje mi potrzebną tablicę, co jest bardziej bezpośrednie na rozdzielczość.


Dlaczego użyłeś tutaj operatora rozkładania (...) - ... nowego zestawu? To nie działa
Dmitrij Grinko,

Dmitrij Grinko Wydaje mi się, że odpowiedziałem na twoje pytanie w moim Edit1. Ale nie jestem pewien, co miałeś na myśli mówiąc „to nie działa”, ponieważ obie odpowiedzi mogą ci przeszkodzić
Jeferson Euclides,

10

W tych samych wierszach, co JSON.encode, należy użyć join ().

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    //slice so we do not effect the original
    //sort makes sure they are in order
    //join makes it a string so we can do a string compare
    var cA = arrA.slice().sort().join(","); 
    var cB = arrB.slice().sort().join(",");

    return cA===cB;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];  //will return true

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //true

Jedynym problemem jest to, czy zależy Ci na typach, które testują ostatnie porównania. Jeśli zależy Ci na typach, będziesz musiał zapętlić.

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;

    //slice so we do not effect the orginal
    //sort makes sure they are in order
    var cA = arrA.slice().sort(); 
    var cB = arrB.slice().sort();

    for(var i=0;i<cA.length;i++){
         if(cA[i]!==cB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,b) );  //true
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false

Jeśli kolejność pozostanie niezmieniona, a nie tylko pętla, sortowanie nie jest potrzebne.

function checkArrays( arrA, arrB ){

    //check if lengths are different
    if(arrA.length !== arrB.length) return false;


    for(var i=0;i<arrA.length;i++){
         if(arrA[i]!==arrB[i]) return false;
    }

    return true;

}

var a = [1,2,3,4,5];
var b = [5,4,3,2,1];
var c = [1,2,3,4];
var d = [1,2,3,4,6];
var e = ["1","2","3","4","5"];

console.log( checkArrays(a,a) );  //true
console.log( checkArrays(a,b) );  //false
console.log( checkArrays(a,c) );  //false
console.log( checkArrays(a,d) );  //false
console.log( checkArrays(a,e) );  //false

3
Działa to tylko w przypadku niektórych tablic i będzie bardzo wolne w przypadku dużych tablic.
Tomáš Zato - Przywróć Monikę

2
Generowanie JSON również się zapętla, po prostu (lub wydaje się, że tak) nie wiesz o tym. Oprócz zapętlania, generowanie JSON wymaga również więcej pamięci - przed porównaniem tworzy 2 reprezentacje łańcuchowe wspomnianych tablic. Zaimplementowano funkcję downwote, aby uporządkować odpowiedzi od najlepszych do najgorszych. Myślę, że twoja odpowiedź nie jest dobrą odpowiedzią, więc zrewidowałem ją.
Tomáš Zato - Przywróć Monikę

2
Przepraszam, właśnie powiedziałem JSON zamiast .join(). Być może, jeśli podasz swoje drugie rozwiązanie jako podstawowe (ponieważ jest lepsze, choć bezzębne w przypadku tablic wielowymiarowych), nie oceniłbym cię w ten sposób. Do tej pory zanotowałem wszystkie odpowiedzi, które konwertują tablice na ciągi. Również głosowałem za wszystkimi, którzy używają tego we właściwy sposób, na wypadek, gdybyś musiał to wiedzieć. Oznacza to odpowiedź @Tim Down i Bireys one.
Tomáš Zato - Przywróć Monikę

6
Pierwsza wersja checkArrays([1,2,3] , ["1,2",3]) == truekończy się niepowodzeniem: i jest bardzo mało prawdopodobne, że tak właśnie się stanie!
Doin

2
@epascarello: Tak, możesz, ale (oprócz nieefektywności bardzo długiego separatora, który sugerujesz) oznacza to, że będą przypadki krawędzi (gdzie tablica zawiera ciąg z separatorem), w których funkcja checkArrays () źle zachowuje się . Nie może to stanowić problemu, jeśli wiesz coś o zawartości tablic (więc możesz wybrać separator, na pewno nie będzie w elementach tablicy), ale jeśli próbujesz napisać ogólne porównanie tablic funkcja, a następnie użycie w join()ten sposób powoduje subtelny błąd!
Doin

7

Oto wersja maszynopisu:

//https://stackoverflow.com/a/16436975/2589276
export function arraysEqual<T>(a: Array<T>, b: Array<T>): boolean {
    if (a === b) return true
    if (a == null || b == null) return false
    if (a.length != b.length) return false

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false
    }
    return true
}

//https://stackoverflow.com/a/16436975/2589276
export function arraysDeepEqual<T>(a: Array<T>, b: Array<T>): boolean {
    return JSON.stringify(a) === JSON.stringify(b)
}

Niektóre przypadki testowe dla mokki:

it('arraysEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']

    expect(arraysEqual(a, b)).to.equal(true)
    expect(arraysEqual(c, d)).to.equal(true)
    expect(arraysEqual(a, d)).to.equal(false)
    expect(arraysEqual(e, f)).to.equal(true)
    expect(arraysEqual(f, g)).to.equal(false)
})

it('arraysDeepEqual', function () {
    let a = [1,2]
    let b = [1,2]
    let c = [2,3]
    let d = [2, 3]
    let e = ['car','apple','banana']
    let f = ['car','apple','banana']
    let g = ['car','apple','banan8']
    let h = [[1,2],'apple','banan8']
    let i = [[1,2],'apple','banan8']
    let j = [[1,3],'apple','banan8']

    expect(arraysDeepEqual(a, b)).to.equal(true)
    expect(arraysDeepEqual(c, d)).to.equal(true)
    expect(arraysDeepEqual(a, d)).to.equal(false)
    expect(arraysDeepEqual(e, f)).to.equal(true)
    expect(arraysDeepEqual(f, g)).to.equal(false)
    expect(arraysDeepEqual(h, i)).to.equal(true)
    expect(arraysDeepEqual(h, j)).to.equal(false)
})

6

Jeśli używasz środowiska testowego, takiego jak Mocha, z biblioteką asercji Chai , możesz użyć głębokiej równości do porównania tablic.

expect(a1).to.deep.equal(a2)

Powinno to zwrócić wartość prawdy tylko wtedy, gdy tablice mają równe elementy przy odpowiednich indeksach.


6

Jeśli są to dwie tablice liczb lub ciągów znaków, jest to szybka jednowierszowa

const array1 = [1, 2, 3];
const array2 = [1, 3, 4];
console.log(array1.join(',') === array2.join(',')) //false

const array3 = [1, 2, 3];
const array4 = [1, 2, 3];
console.log(array3.join(',') === array4.join(',')) //true

const array1 = [1]; const array2 = [1, 1]; console.log (array1.join ('') === array2.join ('')) // zwraca true
Dan M.

nie powinno: tablica1.join („”) to „1”, a tablica2.join („”) to „11”
Gaizka Allende

przepraszam, literówka. Pierwsza tablica powinna być [11]. Dość oczywiste, dlaczego tak się dzieje i jak to naprawić.
Dan M.

Nie jestem pewien, o co ci chodzi, to całkiem proste: [1] .join () to „1”, a [1,1] .join () to „1,1”, więc nigdy nie będą sobie równe
Gaizka Allende

proszę, przeczytaj mój komentarz jeszcze uważniej. Jeśli nadal go nie widzisz, zrób łupy na ideone.com/KFu427
Dan M.

5

W moim przypadku porównywane tablice zawierają tylko liczby i ciągi znaków. Ta funkcja pokaże, czy tablice zawierają te same elementy.

function are_arrs_match(arr1, arr2){
    return arr1.sort().toString() === arr2.sort().toString()
}

Przetestujmy to!

arr1 = [1, 2, 3, 'nik']
arr2 = ['nik', 3, 1, 2]
arr3 = [1, 2, 5]

console.log (are_arrs_match(arr1, arr2)) //true
console.log (are_arrs_match(arr1, arr3)) //false

Pytanie nie wymaga sortowania, więc twoje rozwiązanie jest niewłaściwe dla takich przykładów are_arrs_equal([1,2], [2,1]). Zobacz także inne dyskusje na tej stronie, aby dowiedzieć się, dlaczego tworzenie łańcuchów jest niepotrzebne, kruche i niewłaściwe.
traktuj swoje mody dobrze

are_arrs_equal([1,2], [2,1])zwraca truezgodnie z oczekiwaniami. Być może to rozwiązanie nie jest idealne, ale zadziałało dla mnie.
yesnik

Taki właśnie jest problem, te dwa nie są równe w żadnym sensie słowa „równy” dla uporządkowanej struktury danych. Są to tablice, a nie zbiory, a jeśli chcesz ustawić równość, powinieneś to tak nazwać - i odpowiadać na inne pytanie. :-)
traktuj swoje mody dobrze

1
Zgadzam się z powyższymi komentarzami, ale to rozwiązanie działa również dla mnie w moich prostych tablicach liczb całkowitych, w których kolejność nie jest ważna, więc z niej skorzystam.
tomazahlin

1
Nie odpowiada za are_arrs_match([1,2], ["1,2"])(zwroty true). I pamiętaj, że the sort()wywołanie zmodyfikuje tablice wejściowe - może to nie być pożądane.
try-catch-wreszcie

5

Porównuje to 2 nieposortowane tablice:

function areEqual(a, b) {
  if ( a.length != b.length) {
    return false;
  }
  return a.filter(function(i) {
    return !b.includes(i);
  }).length === 0;  
}

Chociaż jest to kosztowne (pod względem zasobów obliczeniowych), jest to solidne rozwiązanie, które powinno być dobre dla różnych typów i nie opiera się na sortowaniu!
użytkownik3.1415927


4

Możemy to zrobić funkcjonalnie, używając every( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every )

function compareArrays(array1, array2) {
    if (array1.length === array2.length)
        return array1.every((a, index) => a === array2[index])
    else
        return false
}

// test
var a1 = [1,2,3];
var a2 = [1,2,3];

var a3 = ['a', 'r', 'r', 'a', 'y', '1']
var a4 = ['a', 'r', 'r', 'a', 'y', '2']

console.log(compareArrays(a1,a2)) // true
console.log(compareArrays(a1,a3)) // false
console.log(compareArrays(a3,a4)) // false

4

Twój kod nie będzie odpowiednio traktował sprawy, gdy obie tablice mają te same elementy, ale nie w tej samej kolejności.

Spójrz na mój kod z twoim przykładem, który porównuje dwie tablice, których elementami są liczby, możesz go zmodyfikować lub rozszerzyć dla innych typów elementów (używając .join () zamiast .toString ()).

var a1 = [1,2,3];
var a2 = [1,2,3];
const arraysAreEqual = a1.sort().toString()==a2.sort().toString();
// true if both arrays have same elements else false
console.log(arraysAreEqual);


3

Oto moje rozwiązanie:

/**
 * Tests two data structures for equality
 * @param {object} x
 * @param {object} y
 * @returns {boolean}
 */
var equal = function(x, y) {
    if (typeof x !== typeof y) return false;
    if (x instanceof Array && y instanceof Array && x.length !== y.length) return false;
    if (typeof x === 'object') {
        for (var p in x) if (x.hasOwnProperty(p)) {
            if (typeof x[p] === 'function' && typeof y[p] === 'function') continue;
            if (x[p] instanceof Array && y[p] instanceof Array && x[p].length !== y[p].length) return false;
            if (typeof x[p] !== typeof y[p]) return false;
            if (typeof x[p] === 'object' && typeof y[p] === 'object') { if (!equal(x[p], y[p])) return false; } else
            if (x[p] !== y[p]) return false;
        }
    } else return x === y;
    return true;
};

Działa z dowolną zagnieżdżoną strukturą danych i oczywiście ignoruje metody obiektów. Nawet nie myśl o rozszerzeniu Object.prototype za pomocą tej metody, gdy raz spróbowałem, jQuery się zepsuł;)

W przypadku większości macierzy jest to wciąż szybsze niż w przypadku większości rozwiązań do serializacji. Jest to prawdopodobnie najszybsza metoda porównywania tablic rekordów obiektów.


nie dobrze! te dają prawdę: equal({}, {a:1})i equal({}, null)to pomija błędy:equal({a:2}, null)
kristianlm

3
JSON.stringify(collectionNames).includes(JSON.stringify(sourceNames)) ?  array.push(collection[i]) : null

Tak to zrobiłem.


Dobre rozwiązanie - ale w niektórych sytuacjach zastanawiam się, czy nie zawsze będzie działać zgodnie z przeznaczeniem, na przykład w przypadku niektórych prymitywów lub głęboko zagnieżdżonych tablic? Mam nadzieję, że zadziała w każdych okolicznościach
Ben Rondeau,

3

Porównywanie 2 tablic:

var arr1 = [1,2,3];
var arr2 = [1,2,3];

function compare(arr1,arr2)
{
  if((arr1 == arr2) && (arr1.length == arr2.length))
    return true;
  else
    return false;
}

funkcja wywoływania

var isBool = compare(arr1.sort().join(),arr2.sort().join());

Ta odpowiedź nie zadziała, ponieważ === nie zachowuje się zgodnie z oczekiwaniami dla tablic.
Michael Yang

Odpowiedź działa, chociaż === nie ma tutaj żadnego znaczenia (ponieważ sort () działa tylko na tablicy). Nawet == będzie również działać.
Amay Kulkarni

Wypróbuj sam; wypisuje fałsz, jeśli uruchomisz ten kod. Wynika to z porównania wartości referencyjnych tablic o wartości == i === zamiast ich wartości rzeczywistych. == i === są przeznaczone tylko do pierwotnego porównania typów.
Michael Yang,

Zwraca prawdę, użyliśmy go, ale usunąłem teraz „===”, ponieważ nie jest to konieczne
Amay Kulkarni

Ach, nie zauważyłem, że konwertujesz na ciąg znaków i wywołujesz funkcję po sortowaniu i dołączeniu; moje przeprosiny.
Michael Yang,

3

Wierzę w prosty JSi zrozumiały sposób ECMAScript 2015, który jest słodki i prosty do zrozumienia.

var is_arrays_compare_similar = function (array1, array2) {

    let flag = true;

    if (array1.length == array2.length) {

        // check first array1 object is available in array2 index
        array1.every( array_obj => {
            if (flag) {
                if (!array2.includes(array_obj)) {
                    flag = false;
                }
            }
        });

        // then vice versa check array2 object is available in array1 index
        array2.every( array_obj => {
            if (flag) {
                if (!array1.includes(array_obj)) {
                    flag = false;
                }
            }
        });

        return flag;
    } else {
        return false;
    }

}

mam nadzieję, że to komuś pomoże.


1
Dlaczego potrzebna jest kontrola vice versa? Wiemy, że tablice mają ten sam rozmiar, więc jeśli każdy element w tablicy1 znajdzie się również w tablicy2; dlaczego mielibyśmy wtedy sprawdzać, czy każdy element w tablicy 2 również znajduje się w tablicy 1?
JeffryHouser,

2

Rozszerzenie pomysłu Tomáša Zato. Array.prototype.compare Tomasa powinno być w rzeczywistości nazywane Array.prototype.compareIdentical.

Przekazuje:

[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 2]]) === false;
[1, "2,3"].compareIdentical ([1, 2, 3]) === false;
[1, 2, [3, 4]].compareIdentical ([1, 2, [3, 4]]) === true;
[1, 2, 1, 2].compareIdentical ([1, 2, 1, 2]) === true;

Ale zawiedzie:

[[1, 2, [3, 2]],1, 2, [3, 2]].compareIdentical([1, 2, [3, 2],[1, 2, [3, 2]]])

Oto lepsza (moim zdaniem) wersja:

Array.prototype.compare = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time
    if (this.length != array.length)
        return false;

    this.sort();
    array.sort();
    for (var i = 0; i < this.length; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].compare(array[i]))
                return false;
        }
        else if (this[i] != array[i]) {
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;
        }
    }
    return true;
}

http://jsfiddle.net/igos/bcfCY/


2
-1. Jeśli „nie powiedzie się” na podanym przez ciebie przykładzie, dotyczy to nieco arbitralnej definicji „nie”. Dlaczego miałbyś oczekiwać, że te dwie różne tablice będą uważane za równe? Nie wyjaśniłeś nawet, jaką koncepcję „równości” próbujesz tutaj wdrożyć, ani dlaczego jest to rozsądne lub pomocne, ale wygląda na to, że chcesz porównywać tablice wielowymiarowe tak, jakby były zwinięte w jednowymiarowe te. Jeśli tak, to nawet tego nie osiągnąłeś: [1,2]. Porównaj ([[1,2]]) daje fałsz w twojej wersji, podobnie jak w przypadku Tomáša.
Mark Amery

Na podstawie tego, co mógłbym wywnioskować, mówi on, że [1, 2, 3, 4] i [1, 3, 2, 4] należy porównać jako równe (kolejność nie ma znaczenia).
Gautham Badhrinathan

2
var a1 = [1,2,3,6];
var a2 = [1,2,3,5];

function check(a, b) {
  return (a.length != b.length) ? false : 
  a.every(function(row, index) {
    return a[index] == b[index];
  });
}  

check(a1, a2);

////// LUB ///////

var a1 = [1,2,3,6];
var a2 = [1,2,3,6];

function check(a, b) {
  return (a.length != b.length) ? false : 
  !(a.some(function(row, index) {
    return a[index] != b[index];
  }));
}  

check(a1, a2)

Możesz też mieć jakąś funkcję, która nie powtórzy się całkowicie, jeśli spełnimy wymagany warunek, jak wyżej
Vasanth

2

Inne podejście z bardzo małą liczbą kodów (użycie Array redukowania i Array obejmuje ):

arr1.length == arr2.length && arr1.reduce((a, b) => a && arr2.includes(b), true)

Jeśli chcesz porównać również równość kolejności:

arr1.length == arr2.length && arr1.reduce((a, b, i) => a && arr2[i], true)
  • Te length, zapewnia Sprawdź, czy zestaw elementów w jednej tablicy nie jest tylko podzbiorem drugiego.

  • Reduktor służy do przechodzenia przez jedną tablicę i wyszukiwania każdego elementu w drugiej tablicy. Jeśli jeden element nie zostanie znaleziony, funkcja zmniejszania powróci false.

    1. W pierwszym przykładzie testowane jest włączenie elementu
    2. Drugi przykład sprawdza także kolejność

1
czy możesz wyjaśnić trochę swój kod, aby wyjaśnić tę odpowiedź?
ted

1. porównaj długości tablic, aby upewnić się, że jedna tablica nie jest podzbiorem drugiej
DEls

2. Użyj reduktora, aby przejść przez jedną tablicę i wyszukać każdy element w drugiej tablicy. Jeśli jeden element nie zostanie znaleziony, funkcja zmniejszania zwróci „fałsz”
DEls

@DEls: edytowałem twoje wyjaśnienie w odpowiedzi (nieco przerobione i rozszerzone). Możesz teraz usunąć swoje komentarze i oznaczyć pierwszy i ten jako przestarzały.
try-catch-wreszcie

2

Proste podejście:

function equals(a, b) {
    if ((a && !b) || (!a && b) || (!a && !b) || (a.length !== b.length)) {
        return false;
    }

    var isDifferent = a.some(function (element, index) { 
        return element !== b[index];
    });

    return !isDifferent;
}

2

Już kilka świetnych odpowiedzi, ale chciałbym podzielić się innym pomysłem, który okazał się niezawodny w porównaniu tablic. Możemy porównać dwie tablice za pomocą JSON.stringify () . Stworzy ciąg z tablicy, a tym samym porówna dwa uzyskane ciągi z dwóch tablic dla równości

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:1},2]) //true

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},2]) //false

JSON.stringify([1,{a:1},2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:1},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //false

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4],2]) //true

JSON.stringify([1,{a:2},[3,4],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[4]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //false

JSON.stringify([1,{a:2},[3,4,[5]],2]) == JSON.stringify([1,{a:2},[3,4,[5]],2]) //true

2

Rekurencyjne i działa na tablicach NESTED :

function ArrEQ(a1,a2){
   return( 
        //:Are both elements arrays?
        Array.isArray(a1)&&Array.isArray(a2) 
        ?
        //:Yes: Test each entry for equality:
        a1.every((v,i)=>(ArrEQ(v,a2[i])))
        :
        //:No: Simple Comparison:
        (a1===a2)
   );;
};;

console.log( "Works With Nested Arrays:" );
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]],
    [1,2,3,[4,5,[6,"SAME/IDENTICAL"]]]
));;     
console.log( ArrEQ( 
    [1,2,3,[4,5,[6,"DIFFERENT:APPLES" ]]],
    [1,2,3,[4,5,[6,"DIFFERENT:ORANGES"]]]
));;  

2

Działa z MULTIPLE argumentami z tablicami NESTED :

//:Return true if all of the arrays equal.
//:Works with nested arrays.
function AllArrEQ(...arrays){
    for(var i = 0; i < (arrays.length-1); i++ ){
        var a1 = arrays[i+0];
        var a2 = arrays[i+1];
        var res =( 
            //:Are both elements arrays?
            Array.isArray(a1)&&Array.isArray(a2) 
            ?
            //:Yes: Compare Each Sub-Array:
            //:v==a1[i]
            a1.every((v,i)=>(AllArrEQ(v,a2[i])))
            :
            //:No: Simple Comparison:
            (a1===a2)
        );;
        if(!res){return false;}
    };;
    return( true );
};;

console.log( AllArrEQ( 
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
        [1,2,3,[4,5,[6,"ALL_EQUAL"   ]]],
));; 

2
In a simple way uning stringify but at same time thinking in complex arrays:

**Simple arrays**:  
var a = [1,2,3,4];  
var b = [4,2,1,4];  
JSON.stringify(a.sort()) === JSON.stringify(b.sort()) // true  

**Complex arrays**:  
var a = [{id:5,name:'as'},{id:2,name:'bes'}];  
var b = [{id:2,name:'bes'},{id:5,name:'as'}];  
JSON.stringify(a.sort(function(a,b) {return a.id - b.id})) === JSON.stringify(b.sort(function(a,b) {return a.id - b.id})) // true  

**Or we can create a sort function**  

function sortX(a,b) {  
return a.id -b.id; //change for the necessary rules  
}  
JSON.stringify(a.sort(sortX)) === JSON.stringify(b.sort(sortX)) // true  
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.