¿Cómo obtener la diferencia entre dos matrices en JavaScript?

754

¿Hay alguna manera de devolver la diferencia entre dos matrices en JavaScript?

Por ejemplo:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

// need ["c", "d"]
John Adawan
fuente
99
¿Simétrico o no simétrico?
Carreras de ligereza en órbita el
Con la nueva función ES6, esto se puede hacer como un simple revestimiento (tomará mucho tiempo poder usarlo en todos los principales navegadores). En cualquier caso, verifique mi respuesta
Salvador Dali
1
Un aspecto importante de la solución es el rendimiento. La complejidad del tiempo asintótico de este tipo de operación, en otros idiomas, es O(a1.length x log(a2.length)): ¿es posible este rendimiento en JavaScript?
Raul

Respuestas:

219

Supongo que está comparando una matriz normal. De lo contrario, debe cambiar el bucle for por un bucle for .. in .

function arr_diff (a1, a2) {

    var a = [], diff = [];

    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }

    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }

    for (var k in a) {
        diff.push(k);
    }

    return diff;
}

console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));

Una solución mejor, si no le importa la compatibilidad con versiones anteriores, es usar el filtro. Pero aún así, esta solución funciona.

Pensador
fuente
46
Esto puede funcionar pero hace tres bucles para lograr lo que se puede hacer en una línea de código utilizando el método de filtro de Array.
Joshaven Potter
99
Para ser claros, esto implementa la diferencia simétrica de a1 y a2 , a diferencia de las otras respuestas publicadas aquí.
200_success
25
Esta no es la mejor respuesta, pero le estoy dando un voto de caridad, para ayudar a compensar los votos negativos injustos. Solo se deberían rechazar las respuestas incorrectas , y si estuviera trabajando en un proyecto con un alcance de navegadores cruzados (ocurren tiempos difíciles), esta respuesta podría incluso ser útil.
Michael Scheper
3
¿Puedo saber qué sucederá cuando var a1 = ['a', 'b'];y var a2 = ['a', 'b', 'c', 'd', 'b'];, devolverá una respuesta incorrecta , es decir, en ['c', 'd', 'b']lugar de ['c', 'd'].
skbly7
44
La forma más rápida es la solución más obviamente ingenua. Probé todas las soluciones propuestas para diferencia simétrica en este hilo, y el ganador es:function diff2(a, b) { var i, la = a.length, lb = b.length, res = []; if (!la) return b; else if (!lb) return a; for (i = 0; i < la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i < lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
Nomaed
1235

Hay una mejor manera de usar ES7:


Intersección

 let intersection = arr1.filter(x => arr2.includes(x));

Diferencia de intersección Diagrama de Venn

Porque [1,2,3] [2,3]rendirá [2,3]. Por otro lado, for [1,2,3] [2,3,5]devolverá lo mismo.


Diferencia

let difference = arr1.filter(x => !arr2.includes(x));

Diferencia correcta Diagrama de Venn

Porque [1,2,3] [2,3]rendirá [1]. Por otro lado, for [1,2,3] [2,3,5]devolverá lo mismo.


Para una diferencia simétrica , puedes hacer:

let difference = arr1
                 .filter(x => !arr2.includes(x))
                 .concat(arr2.filter(x => !arr1.includes(x)));

Diagrama de Venn de diferencia simétrica

De esta manera, obtendrá una matriz que contiene todos los elementos de arr1 que no están en arr2 y viceversa.

Como @Joshaven Potter señaló en su respuesta, puede agregar esto a Array.prototype para que pueda usarse así:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])
Luis Sieira
fuente
3
Prefiero verificar en < 0lugar de== -1
Vic
1
Calcular la Arraydiferencia se llama así set operation, porque la búsqueda de propiedades es el trabajo propio de Sets, que son órdenes de magnitud más rápido que indexOf/ includes. En pocas palabras, su solución es muy ineficiente y bastante lenta.
@ftor pero con Set, los valores tienen que ser únicos, ¿no?
CervEd
1
@LuisSieira Entiendo que funcionaría [1,2,3] [2,3,5]dado que los números son únicos, pero si lo dijera [1,1,2,3] [1,2,3,5]y esperara, [1]no podría usarlo Set. Sin embargo, su solución tampoco funcionaría: - / Terminé creando esta función porque no pude encontrar una manera satisfactoria de hacerlo de manera más sucinta. Si tienes alguna idea sobre cómo hacerlo, ¡me encantaría saberlo!
CervEd
3
¿No Array.includes()es la función ES7 en lugar de ES6? (1) (2) - y para continuar, con ES6 podría usar , Array.some()por ejemplo let intersection = aArray.filter(a => bArray.some(b => a === b)), ¿no?
Jari Keinänen
910
Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

////////////////////  
// Examples  
////////////////////

[1,2,3,4,5,6].diff( [3,4,5] );  
// => [1, 2, 6]

["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);  
// => ["test5", "test6"]

Nota indexOf y filter no están disponibles en ie antes de ie9.

Joshaven Potter
fuente
50
El único navegador que importa que no admite filtro e indexOf es IE8. IE9 los soporta a ambos. Entonces no está mal.
Bryan Larsen
14
ie7 y ie8 siguen siendo (desafortunadamente) muy relevantes, sin embargo, puede encontrar el código de polyfill para ambas funciones en el sitio de MDN: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… developer.mozilla.org/en/JavaScript/ Reference / Global_Objects / ... Cargue el código que figura en "compatibilidad" a través de un condicional de IE y BOOM. Ie7 / 8 son compatibles.
1nfiniti
44
Esta solución tiene un tiempo de ejecución de O (n ^ 2), una solución lineal sería mucho más eficiente.
jholloman
75
Si utiliza la función de esta manera: [1,2,3].diff([3,4,5])volverá en [1,2]lugar de [1,2,4,5]hacerlo, por lo que no resuelve el problema en la pregunta original, algo a tener en cuenta.
Bugster
12
@AlinPurcaru ¡No es compatible con navegadores arcaicos! = Incorrecto. Considerando Netscape 2.0, la mayor parte del código JS aquí es "incorrecto" según esta definición. Es una tontería decirlo.
NullUserException
304

Esta es, con mucho, la forma más fácil de obtener exactamente el resultado que está buscando, utilizando jQuery:

var diff = $(old_array).not(new_array).get();

diffahora contiene lo que estaba en old_arrayeso no está ennew_array

superfónico
fuente
44
@ Batman Sí, pero solo si son referencias al mismo objeto ( {a: 1} != {a: 1}) ( prueba )
Matmarbon
¿Es posible usarlo con matrices que contienen datos de un objeto personalizado? Lo intenté en realidad de la misma manera pero no funcionó. Cualquier idea será muy apreciable.
LetMeCodeYou
8
¿Es esto un truco? El doc considera este método como parte de los métodos de elementos DOM y no como un asistente de matriz general. Por lo tanto, podría funcionar de esta manera ahora, pero tal vez no en futuras versiones, ya que no estaba destinado a usarlo de esta manera. Sin embargo, estaría feliz si oficialmente fuera un asistente de matriz general.
robsch
1
@robsch Cuando lo usa .notcon una matriz, jQuery usa su utilidad incorporada.grep() que es específicamente para filtrar matrices. No puedo ver esto cambiando.
superfónico el
1
@vsync Suena como si buscaras una diferencia simétrica
superphonic
158

El método de diferencia en Underscore (o su reemplazo directo , Lo-Dash ) también puede hacer esto:

(R)eturns the values from array that are not present in the other arrays

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]

Al igual que con cualquier función de subrayado, también puede usarla en un estilo más orientado a objetos:

_([1, 2, 3, 4, 5]).difference([5, 2, 10]);
mahemoff
fuente
44
Creo que es una buena solución en términos de rendimiento, especialmente porque lodash y subrayado siguen luchando por la mejor implementación. Además, es compatible con IE6.
mahemoff
44
Cuidado, esta implementación no funcionará para matrices de objetos. Consulte stackoverflow.com/q/8672383/14731 para obtener más información.
Gili
1
Como una de las respuestas menciona allí, funciona si es el mismo objeto, pero no si dos objetos tienen las mismas propiedades. Creo que está bien, ya que las nociones de igualdad varían (por ejemplo, también podría ser un atributo "id" en algunas aplicaciones). Sin embargo, sería bueno si pudieras pasar una prueba de comparación para intersecar ().
mahemoff el
Para la posteridad: Lodash ahora tiene _.differenceBy () que toma una devolución de llamada para hacer la comparación; si está comparando objetos, puede colocar una función que los compare como lo necesite.
SomeCallMeTim
2
Tenga cuidado si el orden de los argumentos se invierte, no funcionará. p.ej. _diferencia ([5, 2, 10], [1, 2, 3, 4, 5]); no puede obtener la diferencia
Russj
79

JavaScript simple

Hay dos posibles interpretaciones para la "diferencia". Te dejaré elegir cuál quieres. Digamos que tienes:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. Si desea obtener ['a'], use esta función:

    function difference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      return result;
    }
  2. Si desea obtener ['a', 'c'](todos los elementos contenidos en uno a1 o a2, pero no en ambos, la llamada diferencia simétrica ), use esta función:

    function symmetricDifference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      for (i = 0; i < a2.length; i++) {
        if (a1.indexOf(a2[i]) === -1) {
          result.push(a2[i]);
        }
      }
      return result;
    }

Lodash / subrayado

Si está usando lodash, puede usar _.difference(a1, a2)(caso 1 arriba) o _.xor(a1, a2)(caso 2).

Si está utilizando Underscore.js, puede usar el _.difference(a1, a2) función para el caso 1.

Conjunto ES6, para matrices muy grandes

El código anterior funciona en todos los navegadores. Sin embargo, para matrices grandes de más de aproximadamente 10,000 artículos, se vuelve bastante lento, porque tiene una complejidad O (n²). En muchos navegadores modernos, podemos aprovechar el Setobjeto ES6 para acelerar las cosas. Lodash usa automáticamente Setcuando está disponible. Si no está usando lodash, use la siguiente implementación, inspirada en la publicación de blog de Axel Rauschmayer :

function difference(a1, a2) {
  var a2Set = new Set(a2);
  return a1.filter(function(x) { return !a2Set.has(x); });
}

function symmetricDifference(a1, a2) {
  return difference(a1, a2).concat(difference(a2, a1));
}

Notas

El comportamiento de todos los ejemplos puede ser sorprendente o no obvio si le interesan los conjuntos -0, +0, NaN o dispersos. (Para la mayoría de los usos, esto no importa).

Jo Liss
fuente
Gracias. Salvaste mi día. Tuve que comparar una matriz de 300K, y su solución "Set" funcionó perfectamente. Esta debería ser la respuesta aceptada.
justadev
52

Para obtener la diferencia simétrica , debe comparar las matrices de ambas maneras (o de todas las formas en caso de múltiples matrices)

ingrese la descripción de la imagen aquí


ES7 (ECMAScript 2016)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => !b.includes(x)),
        ...b.filter(x => !a.includes(x))
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => !unique.includes(x));
    }));
}

ES6 (ECMAScript 2015)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => b.indexOf(x) === -1),
        ...b.filter(x => a.indexOf(x) === -1)
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => unique.indexOf(x) === -1);
    }));
}

ES5 (ECMAScript 5.1)

// diff between just two arrays:
function arrayDiff(a, b) {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var other = i === 1 ? a : b;
        arr.forEach(function(x) {
            if (other.indexOf(x) === -1) {
                diff.push(x);
            }
        });
    })

    return diff;
}

// diff between multiple arrays:
function arrayDiff() {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var others = arrays.slice(0);
        others.splice(i, 1);
        var otherValues = Array.prototype.concat.apply([], others);
        var unique = otherValues.filter(function (x, j) { 
            return otherValues.indexOf(x) === j; 
        });
        diff = diff.concat(arr.filter(x => unique.indexOf(x) === -1));
    });
    return diff;
}

Ejemplo:

// diff between two arrays:
const a = ['a', 'd', 'e'];
const b = ['a', 'b', 'c', 'd'];
arrayDiff(a, b); // (3) ["e", "b", "c"]

// diff between multiple arrays
const a = ['b', 'c', 'd', 'e', 'g'];
const b = ['a', 'b'];
const c = ['a', 'e', 'f'];
arrayDiff(a, b, c); // (4) ["c", "d", "g", "f"]

Diferencia entre matrices de objetos

function arrayDiffByKey(key, ...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter( x =>
            !unique.some(y => x[key] === y[key])
        );
    }));
}

Ejemplo:

const a = [{k:1}, {k:2}, {k:3}];
const b = [{k:1}, {k:4}, {k:5}, {k:6}];
const c = [{k:3}, {k:5}, {k:7}];
arrayDiffByKey('k', a, b, c); // (4) [{k:2}, {k:4}, {k:6}, {k:7}]
Luca Borrione
fuente
51

Un enfoque más limpio en ES6 es la siguiente solución.

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

Diferencia

a2.filter(d => !a1.includes(d)) // gives ["c", "d"]

Intersección

a2.filter(d => a1.includes(d)) // gives ["a", "b"]

Unión disyuntiva (diferencia simétrica)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]
ifelse.codes
fuente
Funciona solo en una dirección. Ahora imagine que a1 = ['a', 'b', 'e']: e no se extraerá.
Imrok
Sí, así es como funciona la diferencia en la teoría de conjuntos. (a2 -a1) lo que está buscando es (a2-a1) + (a1-a2)
ifelse.codes
1
@imrok Creo que esto es lo que está buscando [... a2.filter (d =>! a1.includes (d)), ... (a1.filter (d =>! a2.includes (d)) )]
ifelse.codes
2
Hermosa solución, gracias!
Thiago Alves
40

Podrías usar un Set en este caso. Está optimizado para este tipo de operación (unión, intersección, diferencia).

Asegúrese de que se aplique a su caso, una vez que no permita duplicados.

var a = new JS.Set([1,2,3,4,5,6,7,8,9]);
var b = new JS.Set([2,4,6,8]);

a.difference(b)
// -> Set{1,3,5,7,9}
Samuel Carrijo
fuente
44
¡Parece una buena biblioteca! Qué pena que no se puede descargar sólo la Setfunción sin tener que conseguir todo lo demás ...
Blixt
@Blixt Creo que puede descargarlo todo, y solo incluir el archivo set.js
Samuel Carrijo
Set también se implementa en el cierre de Google. clos-library.googlecode.com/svn/docs/…
Ben Flynn
32
function diff(a1, a2) {
  return a1.concat(a2).filter(function(val, index, arr){
    return arr.indexOf(val) === arr.lastIndexOf(val);
  });
}

Combine ambas matrices, los valores únicos aparecerán solo una vez, por lo que indexOf () será el mismo que lastIndexOf ().

pdbrito
fuente
3
Estoy de acuerdo en que esta es la forma más limpia y simple y agradable de que no requiere tocar un prototipo. "Si no puedes explicárselo a un niño de seis años, no lo entiendes tú mismo". - Albert Einstein
lacostenycoder
18

para restar una matriz de otra, simplemente use el fragmento a continuación:

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

var items = new Array();

items = jQuery.grep(a1,function (item) {
    return jQuery.inArray(item, a2) < 0;
});

Devolverá ['1,' 2 ',' 6 '] que son elementos de la primera matriz que no existen en la segunda.

Por lo tanto, de acuerdo con su ejemplo de problema, el siguiente código es la solución exacta:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var _array = new Array();

_array = jQuery.grep(array2, function (item) {
     return jQuery.inArray(item, array1) < 0;
});
Alabama___
fuente
14

Con la llegada de ES6 con sets y operador splat (al momento de funcionar solo en Firefox, consulte la tabla de compatibilidad ), puede escribir el siguiente delineador:

var a = ['a', 'b', 'c', 'd'];
var b = ['a', 'b'];
var b1 = new Set(b);
var difference = [...new Set([...a].filter(x => !b1.has(x)))];

que dará lugar a [ "c", "d" ].

Salvador Dalí
fuente
Solo es curioso cómo es eso diferente de hacerlob.filter(x => !a.indexOf(x)))
chovy
1
@chovy es diferente en la complejidad del tiempo. Mi solución es O(n + m)su solución es O(n * m)donde nym son longitudes de matrices. Tome listas largas y mi solución se ejecutará en segundos, mientras que la suya tomará horas.
Salvador Dali
¿Qué hay de comparar un atributo de una lista de objetos? ¿Es posible usar esta solución?
chovy
2
a.filter(x => !b1.has(x))es más simple Y tenga en cuenta la especificación sólo requiere la complejidad de ser n * f(m) + mcon f(m)sublinear en promedio. Es mejor que n * m, pero no necesariamente n + m.
Oriol
2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];¿ Por qué estás creando una matriz 'a' duplicada? ¿Por qué está convirtiendo el resultado del filtro en un conjunto y luego nuevamente en una matriz? ¿No es esto equivalente avar difference = a.filter(x => !b1.has(x));
Deepak Mittal
13

Enfoque funcional con ES2015

Calcular differenceentre dos matrices es una de las Setoperaciones. El término ya indica que se Setdebe usar el tipo nativo para aumentar la velocidad de búsqueda. De todos modos, hay tres permutaciones cuando calcula la diferencia entre dos conjuntos:

[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]

Aquí hay una solución funcional que refleja estas permutaciones.

Izquierda difference:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( differencel(xs) (ys) );

Derecha difference:

differenceres trivial Es solo differencelcon argumentos invertidos. Puede escribir una función por conveniencia:const differencer = flip(differencel) . ¡Eso es todo!

Simétrico difference:

Ahora que tenemos el izquierdo y el derecho, implementar la simétrica también differencees trivial:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const concat = y => xs => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// symmetric difference

const difference = ys => xs =>
 concat(differencel(xs) (ys)) (flip(differencel) (xs) (ys));

// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( difference(xs) (ys) );

Supongo que este ejemplo es un buen punto de partida para obtener una impresión de lo que significa la programación funcional:

Programación con bloques de construcción que se pueden conectar de muchas maneras diferentes.


fuente
12

Una solución usando indexOf()estará bien para matrices pequeñas pero a medida que crecen en longitud, el rendimiento del algoritmo se acerca O(n^2). Aquí hay una solución que funcionará mejor para matrices muy grandes mediante el uso de objetos como matrices asociativas para almacenar las entradas de la matriz como claves; también elimina entradas duplicadas automáticamente, pero solo funciona con valores de cadena (o valores que se pueden almacenar de forma segura como cadenas):

function arrayDiff(a1, a2) {
  var o1={}, o2={}, diff=[], i, len, k;
  for (i=0, len=a1.length; i<len; i++) { o1[a1[i]] = true; }
  for (i=0, len=a2.length; i<len; i++) { o2[a2[i]] = true; }
  for (k in o1) { if (!(k in o2)) { diff.push(k); } }
  for (k in o2) { if (!(k in o1)) { diff.push(k); } }
  return diff;
}

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
arrayDiff(a1, a2); // => ['c', 'd']
arrayDiff(a2, a1); // => ['c', 'd']
maerics
fuente
Desea usar Object.hasOwnProperty () siempre que esté haciendo un "for in" en un objeto. De lo contrario, corre el riesgo de recorrer cada campo agregado al prototipo del Objeto predeterminado. (O simplemente el padre de su objeto) También solo necesita dos bucles, uno para la creación de una tabla hash y el otro busca en esa tabla hash.
jholloman
1
@jholloman, estoy respetuosamente en desacuerdo . Ahora que podemos controlar la enumeración de cualquier propiedad, presumiblemente debería incluir cualquier propiedad que obtenga durante la enumeración.
Phrogz
1
@Phrogz Un buen punto si solo te preocupan los navegadores modernos. Desafortunadamente, tengo que volver a IE7 en el trabajo, por lo que la edad de piedra es mi tren de pensamiento predeterminado y no tendemos a usar cuñas.
jholloman
10

La respuesta anterior de Joshaven Potter es genial. Pero devuelve elementos en la matriz B que no están en la matriz C, pero no al revés. Por ejemplo, si var a=[1,2,3,4,5,6].diff( [3,4,5,7]);entonces generará: ==> [1,2,6], pero no [1,2,6,7] , que es la diferencia real entre los dos. Todavía puede usar el código de Potter anterior, pero simplemente vuelva a hacer la comparación una vez al revés también:

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return !(a.indexOf(i) > -1);});
};

////////////////////  
// Examples  
////////////////////

var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
var b=[3,4,5,7].diff([1,2,3,4,5,6]);
var c=a.concat(b);
console.log(c);

Esto debería generar: [ 1, 2, 6, 7 ]

usuario1685068
fuente
9

Otra forma de resolver el problema.

function diffArray(arr1, arr2) {
    return arr1.concat(arr2).filter(function (val) {
        if (!(arr1.includes(val) && arr2.includes(val)))
            return val;
    });
}

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

Además, puede usar la sintaxis de la función de flecha:

const diffArray = (arr1, arr2) => arr1.concat(arr2)
    .filter(val => !(arr1.includes(val) && arr2.includes(val)));

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]
Iurii Golskyi
fuente
7
Array.prototype.difference = function(e) {
    return this.filter(function(i) {return e.indexOf(i) < 0;});
};

eg:- 

[1,2,3,4,5,6,7].difference( [3,4,5] );  
 => [1, 2, 6 , 7]
Riyas TK
fuente
Nunca debe extender un objeto nativo de esta manera. Si el estándar se presenta differencecomo función en una versión futura y esta función tiene una firma de función diferente a la suya, romperá su código o las bibliotecas extranjeras que usen esta función.
t.niese
7

Solución muy simple con la función de filtro de JavaScript:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr = arr1.concat(arr2);
  
    newArr = myArr.filter(function(item){
      return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
    });
   alert(newArr);
}

diffArray(a1, a2);

Yash Thakkar
fuente
6

Qué tal esto:

Array.prototype.contains = function(needle){
  for (var i=0; i<this.length; i++)
    if (this[i] == needle) return true;

  return false;
} 

Array.prototype.diff = function(compare) {
    return this.filter(function(elem) {return !compare.contains(elem);})
}

var a = new Array(1,4,7, 9);
var b = new Array(4, 8, 7);
alert(a.diff(b));

Entonces, de esta manera, puede hacer array1.diff(array2)para obtener su diferencia (Sin embargo, la complejidad de tiempo horrible para el algoritmo - O (array1.length x array2.length) creo)

Gato
fuente
Usar la opción de filtro es una gran idea ... sin embargo, no necesita crear un método contiene para Array. Convertí tu idea en una sola línea ... ¡Gracias por la inspiración!
Joshaven Potter
No necesita definir la función contiene (). JS incluye () hace lo mismo.
Da Man
4

Usando http://phrogz.net/JS/ArraySetMath.js puedes:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var array3 = array2.subtract( array1 );
// ["test5", "test6"]

var array4 = array1.exclusion( array2 );
// ["test5", "test6"]
Phrogz
fuente
4
function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

esto funciona para mi

Estar
fuente
3
  • Solución de JavaScript puro (sin bibliotecas)
  • Compatible con navegadores antiguos (no utiliza filter )
  • O (n ^ 2)
  • fnParámetro de devolución de llamada opcional que le permite especificar cómo comparar elementos de la matriz

function diff(a, b, fn){
    var max = Math.max(a.length, b.length);
        d = [];
    fn = typeof fn === 'function' ? fn : false
    for(var i=0; i < max; i++){
        var ac = i < a.length ? a[i] : undefined
            bc = i < b.length ? b[i] : undefined;
        for(var k=0; k < max; k++){
            ac = ac === undefined || (k < b.length && (fn ? fn(ac, b[k]) : ac == b[k])) ? undefined : ac;
            bc = bc === undefined || (k < a.length && (fn ? fn(bc, a[k]) : bc == a[k])) ? undefined : bc;
            if(ac == undefined && bc == undefined) break;
        }
        ac !== undefined && d.push(ac);
        bc !== undefined && d.push(bc);
    }
    return d;
}

alert(
    "Test 1: " + 
    diff(
        [1, 2, 3, 4],
        [1, 4, 5, 6, 7]
      ).join(', ') +
    "\nTest 2: " +
    diff(
        [{id:'a',toString:function(){return this.id}},{id:'b',toString:function(){return this.id}},{id:'c',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        [{id:'a',toString:function(){return this.id}},{id:'e',toString:function(){return this.id}},{id:'f',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        function(a, b){ return a.id == b.id; }
    ).join(', ')
);

Trevor
fuente
Puede almacenar en caché los valores de longitud para obtener más velocidad. Quería recomendar el acceso a los elementos de la matriz sin verificar la longitud, pero aparentemente esa simple verificación produce una aceleración de casi 100x.
Slotos
No hay razón para almacenar en caché los lengthvalores. Ya es una propiedad simple. jsperf.com/array-length-caching
vp_arth
3

Esto está funcionando: básicamente combina las dos matrices, busca los duplicados y empuja lo que no está duplicado en una nueva matriz, que es la diferencia.

function diff(arr1, arr2) {
  var newArr = [];
  var arr = arr1.concat(arr2);
  
  for (var i in arr){
    var f = arr[i];
    var t = 0;
    for (j=0; j<arr.length; j++){
      if(arr[j] === f){
        t++; 
        }
    }
    if (t === 1){
      newArr.push(f);
        }
  } 
  return newArr;
}

Giorgio Giuliani
fuente
3

// enfoque es6

function diff(a, b) {
  var u = a.slice(); //dup the array
  b.map(e => {
    if (u.indexOf(e) > -1) delete u[u.indexOf(e)]
    else u.push(e)   //add non existing item to temp array
  })
  return u.filter((x) => {return (x != null)}) //flatten result
}
copremesis
fuente
3

Complejidad simétrica y lineal . Requiere ES6.

function arrDiff(arr1, arr2) {
    var arrays = [arr1, arr2].sort((a, b) => a.length - b.length);
    var smallSet = new Set(arrays[0]);

    return arrays[1].filter(x => !smallSet.has(x));
}
Dodgie
fuente
2

Solo pensando ... en aras de un desafío ;-) ¿funcionaría esto ... (para matrices básicas de cadenas, números, etc.) sin matrices anidadas

function diffArrays(arr1, arr2, returnUnion){
  var ret = [];
  var test = {};
  var bigArray, smallArray, key;
  if(arr1.length >= arr2.length){
    bigArray = arr1;
    smallArray = arr2;
  } else {
    bigArray = arr2;
    smallArray = arr1;
  }
  for(var i=0;i<bigArray.length;i++){
    key = bigArray[i];
    test[key] = true;
  }
  if(!returnUnion){
    //diffing
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = null;
      }
    }
  } else {
    //union
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = true;
      }
    }
  }
  for(var i in test){
    ret.push(i);
  }
  return ret;
}

array1 = "test1", "test2","test3", "test4", "test7"
array2 = "test1", "test2","test3","test4", "test5", "test6"
diffArray = diffArrays(array1, array2);
//returns ["test5","test6","test7"]

diffArray = diffArrays(array1, array2, true);
//returns ["test1", "test2","test3","test4", "test5", "test6","test7"]

Tenga en cuenta que la clasificación probablemente no será como se indicó anteriormente ... pero si lo desea, llame a .sort () en la matriz para ordenarlo.

scunliffe
fuente
2

Quería una función similar que tomara una matriz antigua y una matriz nueva y me diera una matriz de elementos agregados y una matriz de elementos eliminados, y quería que fuera eficiente (¡así que no contiene!).

Puedes jugar con mi solución propuesta aquí: http://jsbin.com/osewu3/12 .

¿Alguien puede ver algún problema / mejora de ese algoritmo? ¡Gracias!

Listado de código:

function diff(o, n) {
  // deal with empty lists
  if (o == undefined) o = [];
  if (n == undefined) n = [];

  // sort both arrays (or this won't work)
  o.sort(); n.sort();

  // don't compare if either list is empty
  if (o.length == 0 || n.length == 0) return {added: n, removed: o};

  // declare temporary variables
  var op = 0; var np = 0;
  var a = []; var r = [];

  // compare arrays and add to add or remove lists
  while (op < o.length && np < n.length) {
      if (o[op] < n[np]) {
          // push to diff?
          r.push(o[op]);
          op++;
      }
      else if (o[op] > n[np]) {
          // push to diff?
          a.push(n[np]);
          np++;
      }
      else {
          op++;np++;
      }
  }

  // add remaining items
  if( np < n.length )
    a = a.concat(n.slice(np, n.length));
  if( op < o.length )
    r = r.concat(o.slice(op, o.length));

  return {added: a, removed: r}; 
}
Ian Grainger
fuente
2

Estaba buscando una respuesta simple que no implicara el uso de diferentes bibliotecas, y se me ocurrió una propia que no creo que se haya mencionado aquí. No sé qué tan eficiente es ni nada, pero funciona;

    function find_diff(arr1, arr2) {
      diff = [];
      joined = arr1.concat(arr2);
      for( i = 0; i <= joined.length; i++ ) {
        current = joined[i];
        if( joined.indexOf(current) == joined.lastIndexOf(current) ) {
          diff.push(current);
        }
      }
      return diff;
    }

Para mi código, también necesito sacar duplicados, pero supongo que no siempre es preferible.

Supongo que el principal inconveniente es que potencialmente está comparando muchas opciones que ya han sido rechazadas.

Samuel
fuente
2

littlebit arregla la mejor respuesta

function arr_diff(a1, a2)
{
  var a=[], diff=[];
  for(var i=0;i<a1.length;i++)
    a[a1[i]]=a1[i];
  for(var i=0;i<a2.length;i++)
    if(a[a2[i]]) delete a[a2[i]];
    else a[a2[i]]=a2[i];
  for(var k in a)
   diff.push(a[k]);
  return diff;
}

Esto tendrá en cuenta el tipo de elemento actual. b / c cuando hacemos un [a1 [i]] convierte un valor a cadena desde su valor original, por lo que perdimos el valor real.

Vedanta-krit das
fuente
Esto todavía falla para una serie de objetos. var a = [{a: 1}], b = [{b: 2}] arr_diff (a, b) == [].
EricP