objetos functions array javascript arrays array-difference

functions - javascript array methods



¿Cómo obtener la diferencia entre dos matrices en Javascript? (30)

¿Hay una 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"]

Cualquier consejo muy apreciado.


Enfoque funcional con ES2015.

Calcular la difference entre dos matrices es una de las operaciones de Set . El término ya indica que se debe usar el tipo de Set 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.

difference izquierda

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

difference correcta

differencer es trivial. Es solo differencel con argumentos volteados. Puede escribir una función para su comodidad: const differencer = flip(differencel) . ¡Eso es todo!

difference simétrica:

Ahora que tenemos la izquierda y la derecha, implementar la difference simétrica también se vuelve 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.


JavaScript simple

Hay dos interpretaciones posibles para la "diferencia". Te dejaré elegir el que quieras. Di que tienes:

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

  1. Si quieres obtener [''a''] , usa 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 a1 o a2 , pero no 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á usando Underscore.js, puede usar la función _.difference(a1, a2) para el caso 1.

Set ES6, para matrices muy grandes

El código anterior funciona en todos los navegadores. Sin embargo, para arreglos grandes de más de aproximadamente 10,000 artículos, se vuelve bastante lento, porque tiene complejidad O (n²). En muchos navegadores modernos, podemos aprovechar el objeto ES6 Set para acelerar las cosas. Lodash usa automáticamente Set cuando está disponible. Si no está utilizando lodash, use la siguiente implementación, inspirada en la publicación del 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 las matrices -0, +0, NaN o dispersas. (Para la mayoría de los usos, esto no importa).


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

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 resultará en [ "c", "d" ] .


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

diff ahora contiene lo que estaba en old_array que no está en new_array


Esto está funcionando: básicamente, fusione las dos matrices, busque los duplicados y empuje 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; }


Esto fue inspirado por la respuesta aceptada de Thinker, pero la respuesta de Thinker parece asumir que los arreglos son conjuntos. Se deshace si las matrices son [ "1", "2" ] y [ "1", "1", "2", "2" ]

La diferencia entre esas matrices es [ "1", "2" ] . La siguiente solución es O (n * n), así que no es la ideal, pero si tiene grandes arreglos, también tiene ventajas de memoria sobre la solución de Thinker''s.

Si se trata de conjuntos en primer lugar, la solución de Thinker es definitivamente mejor. Si tiene una versión más reciente de Javascript con acceso a los filtros, también debe usarlos. Esto es solo para aquellos que no están tratando con conjuntos y están usando una versión anterior de JavaScript (por cualquier razón) ...

if (!Array.prototype.diff) { Array.prototype.diff = function (array) { // if the other array is a falsy value, return a copy of this array if ((!array) || (!Array.prototype.isPrototypeOf(array))) { return this.slice(0); } var diff = []; var original = this.slice(0); for(var i=0; i < array.length; ++i) { var index = original.indexOf(array[i]); if (index > -1) { original.splice(index, 1); } else { diff.push(array[i]); } } for (var i=0; i < original.length; ++i) { diff.push(original[i]); } return diff; } }


Hay una mejor manera de usar ES7:

Intersección

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

Para [1,2,3] [2,3] producirá [2,3] . Por otro lado, para [1,2,3] [2,3,5] se devolverá lo mismo.

Diferencia

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

Para [1,2,3] [2,3] producirá [1] . Por otro lado, para [1,2,3] [2,3,5] se devolverá lo mismo.

Por una diferencia simétrica , puedes hacer:

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

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 se pueda utilizar así:

Array.prototype.diff = arr1.filter(x => arr2.includes(x)); [1, 2, 3].diff([2, 3])


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]); luego se mostrará: ==> [1,2,6] , pero no [1,2,6,7] , que es la diferencia real entre los dos. Aún puedes usar el código de Potter arriba, pero simplemente rehacer la comparación una vez hacia atrá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 dar salida: [ 1, 2, 6, 7 ]


Littlebit Fix para 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 en cadena a partir de su valor original, por lo que perdimos el valor real.


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]


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

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.filter( y => x[key] === y[key]).length === 0 ); })); }

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); // () [{k:2}, {k:4}, {k:6}, {k:7}]


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}


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

De esta manera, puede hacer array1.diff(array2) para obtener su diferencia (aunque la complejidad del tiempo es horrible para el algoritmo - O (array1.length x array2.length) creo)


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

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


Si las matrices no son de tipos simples, entonces se puede adaptar una de las respuestas anteriores:

Array.prototype.diff = function(a) { return this.filter(function(i) {return a.map(function(e) { return JSON.stringify(e); }).indexOf(JSON.stringify(i)) < 0;}); };

Este método funciona en matrices de objetos complejos.


Solo pensando ... por un desafío ;-) funcionaría esto ... (para matrices básicas de cadenas, números, etc.) no 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 clasificarla.


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


Supongo que estás comparando una matriz normal. Si no, necesita cambiar el bucle for a un for .. in loop.

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 mejor solución, si no te importa la compatibilidad con versiones anteriores, es usar el filtro. Pero aún así, esta solución funciona.


Una solución que use indexOf() estará bien para arreglos pequeños pero a medida que crezcan, el rendimiento del algoritmo se aproxima a O(n^2) . Aquí hay una solución que funcionará mejor para arreglos muy grandes usando objetos como arreglos asociativos para almacenar las entradas del arreglo como claves; también elimina las entradas duplicadas automáticamente, pero solo funciona con valores de cadena (o valores que pueden almacenarse 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'']


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

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"]


para restar una matriz de otra, simplemente use el siguiente fragmento de código:

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


El método de diferencia en el guión bajo (o su reemplazo Lo-Dash , 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 podría usarlo en un estilo más orientado a objetos:

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


En respuesta a la persona que quería restar una matriz de otra ...

Si no más que decir 1000 elementos prueba esto ...

Configurar una nueva variable para duplicar Array01 y llamarlo Array03.

Ahora, use el algoritmo de clasificación de burbujas para comparar los elementos de Array01 con Array02 y cada vez que encuentre una coincidencia, haga lo siguiente para Array03 ...

if (Array01[x]==Array02[y]) {Array03.splice(x,1);}

NB: ¡Estamos modificando Array03 en lugar de Array01 para no estropear los bucles anidados del tipo burbuja!

Finalmente, copie el contenido de Array03 a Array01 con una tarea simple, y listo.


Estaba buscando una respuesta simple que no implicara el uso de bibliotecas diferentes, y se me ocurrió una que no creo que se haya mencionado aquí. No sé qué tan eficiente es o 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 que se eliminen los duplicados, pero supongo que no siempre es lo preferido.

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


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"]

Array.prototype.diff = function(a) { return this.filter(function(i) {return a.indexOf(i) < 0;}); }; //////////////////// // Examples //////////////////// var dif1 = [1,2,3,4,5,6].diff( [3,4,5] ); console.log(dif1); // => [1, 2, 6] var dif2 = ["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]); console.log(dif2); // => ["test5", "test6"]

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


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]


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


function diff(arr1, arr2) { var filteredArr1 = arr1.filter(function(ele) { return arr2.indexOf(ele) == -1; }); var filteredArr2 = arr2.filter(function(ele) { return arr1.indexOf(ele) == -1; }); return filteredArr1.concat(filteredArr2); } diff([1, "calf", 3, "piglet"], [1, "calf", 3, 4]); // Log ["piglet",4]


function diffArray(arr1, arr2) { var newArr = arr1.concat(arr2); return newArr.filter(function(i){ return newArr.indexOf(i) == newArr.lastIndexOf(i); }); }

esto me funciona


  • Solución de JavaScript puro (sin bibliotecas)
  • Compatible con navegadores antiguos (no usa filter )
  • O (n ^ 2)
  • Parámetro de devolución de llamada fn opcional que le permite especificar cómo comparar elementos de 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('', '') );