Usando jQuery para comparar dois arrays de objetos Javascript


93

Eu tenho duas matrizes de objetos JavaScript que gostaria de comparar para ver se eles são iguais. Os objetos podem não estar (e provavelmente não estarão) na mesma ordem em cada array. Cada array não deve ter mais do que 10 objetos. Achei que o jQuery poderia ter uma solução elegante para esse problema, mas não consegui encontrar muito online.

Eu sei que uma $.each(array, function(){})solução aninhada bruta pode funcionar, mas há alguma função embutida que eu não conheço?

Obrigado.

Respostas:


277

Existe uma maneira fácil ...

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

Se o acima retornar verdadeiro, as duas matrizes são iguais, mesmo se os elementos estiverem em ordem diferente.

NOTA: Isso funciona apenas para versões do jquery <3.0.0 ao usar objetos JSON


12
+1, no entanto, é igual a verdadeiro quando você compara um Array com um objeto que contém as mesmas propriedades, f.ex[0,1] == {0:0,1:1,length=2}
David Hellsing

4
porque $(arr1).not(arr2).length == 0não é suficiente?
Alexxus

10
@Alexxus, você precisa de ambas as comparações porque noté uma função de filtro, não uma função de igualdade. Experimente com [1,2]e [1]. Em uma ordem, você obterá [], e em outra, você receberá [2].
Noyo

3
Observe que se você tiver duplicatas em uma matriz, isso retornará verdadeiro. Portanto, algo como: `[1,1,2,2,3,3] == [1,2,3]` é verdade.
Philll_t

3
jQuery notnão funciona mais com objetos genéricos desde 3.0.0-rc1. Consulte github.com/jquery/jquery/issues/3147
Marc-André Lafortune

35

Eu também estava procurando por isso hoje e encontrei: http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256BFB0077DFFD

Não sei se essa é uma boa solução, embora eles mencionem algumas considerações de desempenho levadas em consideração.

Gosto da ideia de um método auxiliar jQuery. @David Eu prefiro ver seu método de comparação funcionar como:

jQuery.compare(a, b)

Não faz sentido para mim fazer:

$(a).compare(b)

onde aeb são matrizes. Normalmente, quando você $ (algo), você estaria passando uma string de seletor para trabalhar com elementos DOM.

Também em relação à classificação e 'armazenamento em cache' dos arrays classificados:

  • Não acho que classificar uma vez no início do método em vez de todas as vezes no loop seja 'cache'. A classificação ainda acontecerá sempre que você chamar compare (b). Isso é apenas semântica, mas ...
  • for (var i = 0; t [i]; i ++) { ... este loop termina mais cedo se sua matriz t contiver um valor falso em algum lugar, então $ ([1, 2, 3, 4]). compare ( [1, falso, 2, 3]) retorna verdadeiro !
  • Mais importante, o método sort () de array classifica o array no lugar , portanto, fazer var b = t.sort () ... não cria uma cópia classificada do array original, ele classifica o array original e também atribui uma referência a isso em b . Não acho que o método de comparação deva ter efeitos colaterais.

Parece que o que precisamos fazer é copiar os arrays antes de trabalhar neles. A melhor resposta que pude encontrar sobre como fazer isso de uma forma jQuery foi por ninguém menos que John Resig aqui no SO! Qual é a maneira mais eficiente de clonar profundamente um objeto em JavaScript? (veja os comentários em sua resposta para a versão de array da receita de clonagem de objetos)

Nesse caso, acho que o código para isso seria:

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]

2
na verdade, eu provavelmente chamaria esse método de 'arrayCompare' em vez de 'compare', pois essa é a única coisa em que ele foi projetado para funcionar ...
Anentrópico

Local de fato. Muito obrigado.
dimitarvp

14

Minha abordagem foi bem diferente - eu aplainei ambas as coleções usando JSON.stringify e usei uma string normal compare para verificar a igualdade.

Ie

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

NB: Por favor, note que seu método assume que ambas as coleções são classificadas de maneira semelhante, caso contrário, você obteria um resultado falso!


1
Eu precisava de um método simples para comparar arrays em meus testes de unidade, garantindo a mesma ordem para os dois arrays. É muito bom, obrigado.
aymericbeaumet de

Obrigado, você salvou meu dia
Sourabh Kumar Sharma

12

Converta ambos array em string e compare

if (JSON.stringify(array1) == JSON.stringify(array2))
{
    // your code here
}

1
Parece bom para comparar matrizes aninhadas e quando a ordem é importante.
Pierre de LESPINAY

3

Eu encontrei essa discussão porque precisava de uma maneira de comparar profundamente arrays e objetos. Usando os exemplos aqui, eu vim com o seguinte (dividido em 3 métodos para maior clareza):

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

Testando

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

3

No meu caso, as matrizes comparadas contêm apenas números e strings . Esta solução funcionou para mim:

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

Vamos testar!

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

1

Não acho que haja uma boa maneira "jQuery" de fazer isso, mas se você precisar de eficiência, mapeie um dos arrays por uma determinada chave (um dos campos de objeto únicos) e, em seguida, faça a comparação percorrendo o outro array e comparando com o mapa, ou array associativo, que você acabou de construir.

Se a eficiência não for um problema, basta comparar todos os objetos em A com todos os objetos em B. Contanto que | A | e | B | são pequenos, você deve estar bem.


@Stefan, obrigado pela resposta rápida. Você pode postar código de exemplo para sua ideia de mapeamento? Obrigado.
MegaMatt de

1

Bem, se você quiser comparar apenas o conteúdo de arrays, há uma função jQuery útil $ .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

Boa solução, mas não tenho certeza se isso está levando em consideração os arrays com os mesmos elementos, mas em uma ordem diferente.
MegaMatt de

Você pode mover se (! Igual) retornar falso; até o final de $ .each para evitar disparar a função novamente. E você pode retornar igual; para evitar uma comparação.
Matt

1

Mude a matriz para string e compare

var arr = [1,2,3], 
arr2 = [1,2,3]; 
console.log(arr.toString() === arr2.toString());

1

O bom forro de Sudhakar R como método global 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;
  }
});

0

Eu também descobri isso ao tentar fazer algumas comparações de array com jQuery. No meu caso, eu tinha strings que sabia serem arrays:

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

Mas eu me importava se era uma correspondência completa ou apenas parcial, então usei algo como o seguinte, baseado na resposta de Sudhakar 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';
  }
}

0

Se duplicatas importam de forma que [1, 1, 2]não deveriam ser iguais, [2, 1]mas deveriam ser iguais [1, 2, 1], aqui está uma solução de contagem de referência:

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

Aqui está o violino para brincar .


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>


-3

Tente isto

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

Isso modifica a e b, e só compara o primeiro elemento. wtf.
ScottJ

1
@ScottJ Mr.AH A questão é comparar dois arrays .. Após classificar se os dois arrays são iguais a [0] == b [0]. wtf você bah.
Exceção de

2
Receio não saber o que significa AH e o Google não ajudou. Mas se meu comentário foi tão incorreto, por que este código foi completamente reescrito em 21 de fevereiro?
ScottJ

@ScottJ Muito provavelmente AH === wtf
Exceção de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.