Usando jQuery para comparar dos matrices de objetos Javascript


93

Tengo dos matrices de objetos JavaScript que me gustaría comparar para ver si son iguales. Es posible que los objetos no estén (y probablemente no lo estarán) en el mismo orden en cada matriz. Cada matriz no debe tener más de 10 objetos. Pensé que jQuery podría tener una solución elegante para este problema, pero no pude encontrar mucho en línea.

Sé que una $.each(array, function(){})solución anidada bruta podría funcionar, pero ¿hay alguna función incorporada que no conozca?

Gracias.

Respuestas:


277

Hay una manera fácil ...

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

Si lo anterior devuelve verdadero, ambas matrices son iguales incluso si los elementos están en un orden diferente.

NOTA: Esto solo funciona para las versiones de jquery <3.0.0 cuando se usan objetos JSON


12
+1, sin embargo, es igual a verdadero cuando se compara una matriz con un objeto que contiene las mismas propiedades, por ejemplo[0,1] == {0:0,1:1,length=2}
David Hellsing

4
¿ $(arr1).not(arr2).length == 0Por qué no es suficiente?
Alexxus

10
@Alexxus, necesita ambas comparaciones porque notes una función de filtro, no una función de igualdad. Pruébelo con [1,2]y [1]. En un orden, obtendrá [], y en otro, obtendrá [2].
Noyo

3
Tenga en cuenta que si tiene duplicados en una matriz, esto devolverá verdadero. Entonces, algo como: `[1,1,2,2,3,3] == [1,2,3]` es cierto.
Philll_t

3
jQuery notya no funciona con objetos genéricos a partir de 3.0.0-rc1. Ver github.com/jquery/jquery/issues/3147
Marc-André Lafortune

35

También estaba buscando esto hoy y encontré: http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256BFB0077DFFD

No sé si es una buena solución, aunque mencionan algunas consideraciones de rendimiento que se tienen en cuenta.

Me gusta la idea de un método auxiliar de jQuery. @David Preferiría ver que tu método de comparación funcione así:

jQuery.compare(a, b)

No tiene sentido para mí hacer:

$(a).compare(b)

donde ayb son matrices. Normalmente, cuando $ (algo) estaría pasando una cadena de selección para trabajar con elementos DOM.

También con respecto a ordenar y 'almacenar en caché' las matrices ordenadas:

  • No creo que ordenar una vez al comienzo del método en lugar de cada vez a través del ciclo sea 'almacenamiento en caché'. La ordenación seguirá ocurriendo cada vez que llame a compare (b). Eso es solo semántica, pero ...
  • for (var i = 0; t [i]; i ++) { ... este ciclo termina antes si su matriz t contiene un valor falso en alguna parte, entonces $ ([1, 2, 3, 4]). compare ( [1, falso, 2, 3]) devuelve verdadero .
  • Más importante aún, el método array sort () ordena la matriz en su lugar , por lo que hacer var b = t.sort () ... no crea una copia ordenada de la matriz original, ordena la matriz original y también asigna una referencia a en b . No creo que el método de comparación deba tener efectos secundarios.

Parece que lo que tenemos que hacer es copiar las matrices antes de trabajar en ellas. ¡La mejor respuesta que pude encontrar sobre cómo hacer eso de una manera jQuery fue nada menos que John Resig aquí en SO! ¿Cuál es la forma más eficiente de clonar en profundidad un objeto en JavaScript? (vea los comentarios sobre su respuesta para la versión de matriz de la receta de clonación de objetos)

En cuyo caso, creo que el código sería:

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
en realidad, probablemente llamaría a este método 'arrayCompare' en lugar de 'comparar', ya que eso es lo único en lo que está diseñado para funcionar ...
Anentropic

En efecto. Muchas gracias.
dimitarvp

14

Mi enfoque fue bastante diferente: aplané ambas colecciones usando JSON.stringify y usé una cadena de comparación normal para verificar la igualdad.

Es decir

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: Tenga en cuenta que este método asume que ambas colecciones están ordenadas de manera similar, si no, ¡le daría un resultado falso!


1
Necesitaba un método simple para comparar matrices en mis pruebas unitarias, asegurando el mismo orden para las dos matrices. Es muy lindo, gracias.
aymericbeaumet

Gracias, me salvaste el día
Sourabh Kumar Sharma

12

Convierta ambos arreglos en cadena y compare

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

1
Parece bueno para comparar matrices anidadas y cuando el orden es importante.
Pierre de LESPINAY

3

Encontré esta discusión porque necesitaba una forma de comparar en profundidad matrices y objetos. Usando los ejemplos aquí, se me ocurrió lo siguiente (dividido en 3 métodos para mayor claridad):

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

Pruebas

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

En mi caso, las matrices comparadas contienen solo números y cadenas . Esta solución funcionó para mí:

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

¡Probémoslo!

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

No creo que haya una buena forma "jQuery" de hacer esto, pero si necesita eficiencia, asigne una de las matrices con una determinada clave (uno de los campos de objeto único) y luego haga una comparación recorriendo la otra matriz y comparando con el mapa, o matriz asociativa, que acaba de crear.

Si la eficiencia no es un problema, simplemente compare cada objeto en A con cada objeto en B. Siempre que | A | y | B | son pequeños, debería estar bien.


@ Stefan, gracias por la rápida respuesta. ¿Puede publicar código de ejemplo para su idea de mapeo? Gracias.
MegaMatt

1

Bueno, si desea comparar solo el contenido de las matrices, hay una función 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

Buena solución, pero no estoy seguro de que esto tenga en cuenta que las matrices tienen los mismos elementos, pero en un orden diferente.
MegaMatt

Puede moverse si (! Equal) devuelve falso; hasta la parte inferior de $ .each para evitar volver a activar la función. Y puedes volver igual; para evitar una comparación.
Matt

1

Cambiar matriz a cadena y comparar

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

1

El bonito trazador de líneas 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

También encontré esto cuando buscaba hacer algunas comparaciones de matrices con jQuery. En mi caso, tenía cadenas que sabía que eran matrices:

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

Pero me importaba si era una coincidencia completa o solo una coincidencia parcial, así que usé algo como lo siguiente, basado en la respuesta 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

Si los duplicados importan de manera que [1, 1, 2]no deberían ser iguales [2, 1]pero deberían ser iguales [1, 2, 1], aquí hay una solución de recuento de referencia:

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

Aquí está el violín para jugar .


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

Prueba esto

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

Eso modifica tanto a como b, y solo compara el primer elemento. wtf.
ScottJ

1
@ScottJ Mr.AH La pregunta es comparar dos matrices ... Después de ordenar si ambas matrices son iguales a [0] == b [0]. wtf you bah.
Excepción del

2
Me temo que no sé lo que significa AH, y Google no fue de ayuda. Pero si mi comentario fue tan erróneo, ¿por qué este código fue reescrito por completo el 21 de febrero?
ScottJ

@ScottJ Probablemente AH === wtf
Excepción
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.