Używanie jQuery do porównywania dwóch tablic obiektów JavaScript

93

Mam dwie tablice obiektów JavaScript, które chciałbym porównać, aby sprawdzić, czy są takie same. Obiekty mogą nie być (i najprawdopodobniej nie będą) być w tej samej kolejności w każdej tablicy. Każda tablica nie powinna zawierać więcej niż 10 obiektów. Myślałem, że jQuery może mieć eleganckie rozwiązanie tego problemu, ale nie mogłem znaleźć zbyt wiele w Internecie.

Wiem, że zagnieżdżone $.each(array, function(){})rozwiązanie brutalne może działać, ale czy jest jakaś wbudowana funkcja, której nie jestem świadomy?

Dzięki.

MegaMatt
źródło

Odpowiedzi:

278

Jest łatwy sposób ...

$(arr1).not(arr2).length === 0 && $(arr2).not(arr1).length === 0

Jeśli powyższe zwraca wartość true, obie tablice są takie same, nawet jeśli elementy są w różnej kolejności.

UWAGA: Działa to tylko dla wersji jQuery <3.0.0 podczas korzystania z obiektów JSON

suDocker
źródło
12
+1, jednak jest równe prawdzie, gdy porównujesz tablicę z obiektem, który zawiera te same właściwości, np.[0,1] == {0:0,1:1,length=2}
David Hellsing,
4
dlaczego $(arr1).not(arr2).length == 0to za mało?
Alexxus
10
@Alexxus, potrzebujesz obu porównań, ponieważ notjest to funkcja filtrująca, a nie funkcja równości. Wypróbuj z [1,2]i [1]. W jednej kolejności dostaniesz [], aw innej dostaniesz [2].
Noyo
3
Zauważ, że jeśli masz duplikaty w tablicy, zwróci to wartość true. Więc coś takiego: `[1,1,2,2,3,3] == [1,2,3]` jest prawdą.
Philll_t
3
jQuery notnie działa już z obiektami ogólnymi od 3.0.0-rc1. Zobacz github.com/jquery/jquery/issues/3147
Marc-André Lafortune
35

Tego też szukałem dzisiaj i znalazłem: http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256BFB0077DFFD

Nie wiem, czy to dobre rozwiązanie, chociaż wspominają o uwzględnieniu niektórych aspektów wydajności.

Podoba mi się pomysł metody pomocniczej jQuery. @David Wolałbym, aby Twoja metoda porównania działała tak:

jQuery.compare(a, b)

Nie ma dla mnie sensu robić:

$(a).compare(b)

gdzie a i b to tablice. Normalnie, kiedy $ (coś) , przekazywałbyś łańcuch selektora do pracy z elementami DOM.

Również w odniesieniu do sortowania i „buforowania” posortowanych tablic:

  • Nie sądzę, aby sortowanie raz na początku metody zamiast za każdym razem przez pętlę było „buforowaniem”. Sortowanie będzie nadal odbywać się za każdym razem, gdy wywołasz funkcję porównaj (b). To tylko semantyka, ale ...
  • for (var i = 0; t [i]; i ++) { ... ta pętla kończy się wcześniej, jeśli twoja tablica t zawiera gdzieś fałszywą wartość, więc $ ([1, 2, 3, 4]). porównaj ( [1, false, 2, 3]) zwraca prawdę !
  • Co ważniejsze, metoda array sort () sortuje tablicę na miejscu , więc wykonanie var b = t.sort () ... nie tworzy posortowanej kopii oryginalnej tablicy, sortuje oryginalną tablicę i przypisuje odniesienie do to w b . Nie sądzę, żeby metoda porównania miała skutki uboczne.

Wygląda na to, że musimy skopiować tablice przed rozpoczęciem pracy nad nimi. Najlepszą odpowiedzią, jaką mogłem znaleźć, jak to zrobić w sposób jQuery, był nikt inny jak John Resig tutaj na SO! Jaki jest najbardziej efektywny sposób głębokiego klonowania obiektu w JavaScript? (zobacz komentarze do jego odpowiedzi dla wersji tablicowej receptury klonowania obiektów)

W takim przypadku myślę, że kod byłby taki:

jQuery.extend({
    compare: function (arrayA, arrayB) {
        if (arrayA.length != arrayB.length) { return false; }
        // sort modifies original array
        // (which are passed by reference to our method!)
        // so clone the arrays before sorting
        var a = jQuery.extend(true, [], arrayA);
        var b = jQuery.extend(true, [], arrayB);
        a.sort(); 
        b.sort();
        for (var i = 0, l = a.length; i < l; i++) {
            if (a[i] !== b[i]) { 
                return false;
            }
        }
        return true;
    }
});

var a = [1, 2, 3];
var b = [2, 3, 4];
var c = [3, 4, 2];

jQuery.compare(a, b);
// false

jQuery.compare(b, c);
// true

// c is still unsorted [3, 4, 2]
Anentropowy
źródło
2
właściwie nazwałbym tę metodę raczej „arrayCompare” niż „compare”, ponieważ jest to jedyna rzecz, na której jest przeznaczona ...
Anentropic
Rzeczywiście. Wielkie dzięki.
dimitarvp
14

Moje podejście było zupełnie inne - spłaszczyłem obie kolekcje za pomocą JSON.stringify i użyłem zwykłego porównania ciągów, aby sprawdzić równość.

To znaczy

var arr1 = [
             {Col: 'a', Val: 1}, 
             {Col: 'b', Val: 2}, 
             {Col: 'c', Val: 3}
           ];

var arr2 = [
             {Col: 'x', Val: 24}, 
             {Col: 'y', Val: 25}, 
             {Col: 'z', Val: 26}
           ];

if(JSON.stringify(arr1) == JSON.stringify(arr2)){
    alert('Collections are equal');
}else{
    alert('Collections are not equal');
}

Uwaga: należy pamiętać, że jego metoda zakłada, że ​​obie kolekcje są sortowane w podobny sposób, jeśli nie, dałoby to fałszywy wynik!

Kwex
źródło
1
Potrzebowałem prostej metody porównywania tablic w moich testach jednostkowych, zapewniającej tę samą kolejność dla dwóch tablic. To bardzo miłe, dzięki.
aymericbeaumet
Dzięki, uratowałeś mi dzień
Sourabh Kumar Sharma
12

Przekonwertuj tablicę na ciąg i porównaj

if (JSON.stringify(array1) == JSON.stringify(array2))
{
    // your code here
}
gmsi
źródło
1
Wydaje się dobre do porównywania zagnieżdżonych tablic i gdy ważna jest kolejność.
Pierre de LESPINAY
3

Znalazłem tę dyskusję, ponieważ potrzebowałem sposobu na głębokie porównanie tablic i obiektów. Korzystając z przykładów tutaj, wymyśliłem następujące (podzielone na 3 metody dla jasności):

jQuery.extend({
    compare : function (a,b) {
        var obj_str = '[object Object]',
            arr_str = '[object Array]',
            a_type  = Object.prototype.toString.apply(a),
            b_type  = Object.prototype.toString.apply(b);

            if ( a_type !== b_type) { return false; }
            else if (a_type === obj_str) {
                return $.compareObject(a,b);
            }
            else if (a_type === arr_str) {
                return $.compareArray(a,b);
            }
            return (a === b);
        }
});

jQuery.extend({
    compareArray: function (arrayA, arrayB) {
        var a,b,i,a_type,b_type;
        // References to each other?
        if (arrayA === arrayB) { return true;}

        if (arrayA.length != arrayB.length) { return false; }
        // sort modifies original array
        // (which are passed by reference to our method!)
        // so clone the arrays before sorting
        a = jQuery.extend(true, [], arrayA);
        b = jQuery.extend(true, [], arrayB);
        a.sort(); 
        b.sort();
        for (i = 0, l = a.length; i < l; i+=1) {
            a_type = Object.prototype.toString.apply(a[i]);
            b_type = Object.prototype.toString.apply(b[i]);

            if (a_type !== b_type) {
                return false;
            }

            if ($.compare(a[i],b[i]) === false) {
                return false;
            }
        }
        return true;
    }
});

jQuery.extend({
    compareObject : function(objA,objB) {

        var i,a_type,b_type;

        // Compare if they are references to each other 
        if (objA === objB) { return true;}

        if (Object.keys(objA).length !== Object.keys(objB).length) { return false;}
        for (i in objA) {
            if (objA.hasOwnProperty(i)) {
                if (typeof objB[i] === 'undefined') {
                    return false;
                }
                else {
                    a_type = Object.prototype.toString.apply(objA[i]);
                    b_type = Object.prototype.toString.apply(objB[i]);

                    if (a_type !== b_type) {
                        return false; 
                    }
                }
            }
            if ($.compare(objA[i],objB[i]) === false){
                return false;
            }
        }
        return true;
    }
});

Testowanie

var a={a : {a : 1, b: 2}},
    b={a : {a : 1, b: 2}},
    c={a : {a : 1, b: 3}},
    d=[1,2,3],
    e=[2,1,3];

console.debug('a and b = ' + $.compare(a,b)); // a and b = true
console.debug('b and b = ' + $.compare(b,b)); // b and b = true
console.debug('b and c = ' + $.compare(b,c)); // b and c = false
console.debug('c and d = ' + $.compare(c,d)); // c and d = false
console.debug('d and e = ' + $.compare(d,e)); // d and e = true
Vinny Alves
źródło
3

W moim przypadku porównywane tablice zawierają tylko liczby i łańcuchy . To rozwiązanie zadziałało dla mnie:

function are_arrs_equal(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_equal(arr1, arr2)) //true
console.log (are_arrs_equal(arr1, arr3)) //false
yesnik
źródło
1

Nie wydaje mi się, żeby istniał dobry sposób na "jQuery", aby to zrobić, ale jeśli potrzebujesz wydajności, zamapuj jedną z tablic za pomocą określonego klucza (jednego z unikalnych pól obiektu), a następnie wykonaj porównanie, przechodząc przez drugą tablicę i porównując z mapą lub tablicą asocjacyjną, którą właśnie zbudowałeś.

Jeśli wydajność nie jest problemem, po prostu porównaj każdy obiekt w A z każdym obiektem w B. Tak długo, jak | A | i | B | są małe, powinno być dobrze.

Stefan Kendall
źródło
@Stefan, dzięki za szybką odpowiedź. Czy możesz przesłać przykładowy kod dla swojego pomysłu na mapowanie? Dzięki.
MegaMatt
1

Cóż, jeśli chcesz porównać tylko zawartość tablic, jest przydatna funkcja jQuery $ .inArray ()

var arr = [11, "String #1", 14, "String #2"];
var arr_true = ["String #1", 14, "String #2", 11]; // contents are the same as arr
var arr_false = ["String #1", 14, "String #2", 16]; // contents differ

function test(arr_1, arr_2) {
    var equal = arr_1.length == arr_2.length; // if array sizes mismatches, then we assume, that they are not equal
    if (equal) {
        $.each(arr_1, function (foo, val) {
            if (!equal) return false;
            if ($.inArray(val, arr_2) == -1) {
                equal = false;
            } else {
                equal = true;
            }
        });
    }
    return equal;
}

alert('Array contents are the same? ' + test(arr, arr_true)); //- returns true
alert('Array contents are the same? ' + test(arr, arr_false)); //- returns false
Vilius Paulauskas
źródło
Dobre rozwiązanie, ale nie jestem pewien, czy dotyczy to tablic zawierających te same elementy, ale w innej kolejności.
MegaMatt
Możesz przenieść, jeśli (! Równe) return false; do samego końca $ .each, aby uniknąć ponownego uruchomienia funkcji. I możesz zwrócić równe; aby uniknąć porównania.
Matt
1

Zmień tablicę na ciąg i porównaj

var arr = [1,2,3], 
arr2 = [1,2,3]; 
console.log(arr.toString() === arr2.toString());
sójka
źródło
1

Niezły liner Sudhakar R jako metoda globalna jQuery.

/**
 * Compare two arrays if they are equal even if they have different order.
 *
 * @link https://stackoverflow.com/a/7726509
 */
jQuery.extend({
  /**
   * @param {array} a
   *   First array to compare.
   * @param {array} b
   *   Second array to compare.
   * @return {boolean}
   *   True if both arrays are equal, otherwise false.
   */
  arrayCompare: function (a, b) {
    return $(a).not(b).get().length === 0 && $(b).not(a).get().length === 0;
  }
});
Fleshgrinder
źródło
0

Znalazłem to również, gdy szukałem porównań tablic z jQuery. W moim przypadku miałem ciągi, o których wiedziałem, że są tablicami:

var needle = 'apple orange';
var haystack = 'kiwi orange banana apple plum';

Ale zależało mi na tym, czy to było pełne dopasowanie, czy tylko częściowe dopasowanie, więc użyłem czegoś podobnego do następującego, w oparciu o odpowiedź Sudhakara R.

function compareStrings( needle, haystack ){
  var needleArr = needle.split(" "),
    haystackArr = haystack.split(" "),
    compare = $(haystackArr).not(needleArr).get().length;

  if( compare == 0 ){
    return 'all';
  } else if ( compare == haystackArr.length  ) {
    return 'none';
  } else {
    return 'partial';
  }
}
Korek
źródło
0

Jeśli duplikaty mają takie znaczenie, że [1, 1, 2]nie powinny być równe, [2, 1]ale powinny być równe [1, 2, 1], oto rozwiązanie zliczania odniesienia:

  const arrayContentsEqual = (arrayA, arrayB) => {
    if (arrayA.length !== arrayB.length) {
      return false}

    const refCount = (function() {
      const refCountMap = {};
      const refCountFn = (elt, count) => {
          refCountMap[elt] = (refCountMap[elt] || 0) + count}
      refCountFn.isZero = () => {
        for (let elt in refCountMap) {
          if (refCountMap[elt] !== 0) {
            return false}}
        return true}
      return refCountFn})()

    arrayB.map(eltB => refCount(eltB, 1));
    arrayA.map(eltA => refCount(eltA, -1));
    return refCount.isZero()}

Oto skrzypce do zabawy .

Paul Whipp
źródło
0

var arr1 = [
             {name: 'a', Val: 1}, 
             {name: 'b', Val: 2}, 
             {name: 'c', Val: 3}
           ];

var arr2 = [
             {name: 'c', Val: 3},
             {name: 'x', Val: 4}, 
             {name: 'y', Val: 5}, 
             {name: 'z', Val: 6}
           ];
var _isEqual = _.intersectionWith(arr1, arr2, _.isEqual);// common in both array
var _difference1 = _.differenceWith(arr1, arr2, _.isEqual);//difference from array1 
var _difference2 = _.differenceWith(arr2, arr1, _.isEqual);//difference from array2 
console.log(_isEqual);// common in both array
console.log(_difference1);//difference from array1 
console.log(_difference2);//difference from array2 
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>

Parth
źródło
-3

Spróbuj tego

function check(c,d){
  var a = c, b = d,flg = 0;
  if(a.length == b.length) 
  { 
     for(var i=0;i<a.length;i++) 
           a[i] != b[i] ? flg++ : 0; 
  } 
  else  
  { 
     flg = 1; 
  } 
  return flg = 0;
}
Wyjątek
źródło
To modyfikuje zarówno a, jak i b i porównuje tylko pierwszy element. wtf.
ScottJ,
1
@ScottJ Mr.AH Pytanie polega na porównaniu dwóch tablic .. Po posortowaniu, czy obie tablice są równe a [0] == b [0]. wtf you bah.
Wyjątek
2
Obawiam się, że nie wiem, co oznacza AH, a Google nie pomogło. Ale jeśli mój komentarz był tak poza bazą, dlaczego ten kod został całkowicie przepisany 21 lutego?
ScottJ
@ScottJ Najprawdopodobniej AH === wtf
Wyjątek