Cómo fusionar dos matrices en JavaScript y desduplicar elementos

1368

Tengo dos matrices de JavaScript:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

Quiero que la salida sea:

var array3 = ["Vijendra","Singh","Shakya"];

La matriz de salida debería tener palabras repetidas eliminadas.

¿Cómo fusiono dos matrices en JavaScript para obtener solo los elementos únicos de cada matriz en el mismo orden en que se insertaron en las matrices originales?

Vijjendra
fuente
1
Antes de publicar una nueva respuesta, considere que ya hay más de 75 respuestas para esta pregunta. Por favor, asegúrese de que su respuesta contribuya con información que no se encuentre entre las respuestas existentes.
Janniks
[... nuevo conjunto ([... [1, 2, 3], ... [2, 3, 4]])] resultado [1, 2, 3, 4]
Denis Giffeler
Si desea una solución más genérica que también cubra la fusión profunda, eche un vistazo a esta pregunta . Algunas respuestas también cubren matrices.
Martin Braun

Respuestas:

1667

Para fusionar las matrices (sin eliminar duplicados)

Uso de la versión ES5 Array.concat:

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];

console.log(array1.concat(array2));

Versión ES6 uso desestructuración

const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];

Dado que no hay una forma 'incorporada' de eliminar duplicados ( ECMA-262 realmente tiene lo Array.forEachque sería genial para esto), tenemos que hacerlo manualmente:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
};

Luego, para usarlo:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique(); 

Esto también preservará el orden de las matrices (es decir, no es necesario ordenarlas).

Dado que muchas personas están molestas por el aumento de prototipos Array.prototypey los for inbucles, aquí hay una forma menos invasiva de usarlo:

function arrayUnique(array) {
    var a = array.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
}

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
    // Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));

Para aquellos que tienen la suerte de trabajar con navegadores donde ES5 está disponible, pueden usar Object.definePropertyesto:

Object.defineProperty(Array.prototype, 'unique', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        var a = this.concat();
        for(var i=0; i<a.length; ++i) {
            for(var j=i+1; j<a.length; ++j) {
                if(a[i] === a[j])
                    a.splice(j--, 1);
            }
        }

        return a;
    }
});
LiraNuna
fuente
281
Tenga en cuenta que este algoritmo es O (n ^ 2).
Gumbo
77
Deje [a, b, c]y [x, b, d]sea ​​las matrices (suponga comillas). Concat da [a, b, c, x, b, d]. No sería la salida de unique () [a, c, x, b, d]. Que no conserva el orden Creo - Creo PO quiere[a, b, c, x, d]
Amarghosh
89
OP aceptó la primera respuesta que lo puso a trabajar y parece que firmó. Todavía nos estamos comparando soluciones de los demás, encontrando-n-fijación de fallos, mejorar el rendimiento, asegurándose de que su compatibles en todas partes y así sucesivamente ... La belleza de stackoverflow :-)
Amarghosh
66
Originalmente voté por esto, pero he cambiado de opinión. La asignación de prototipos a Array.prototype tiene las consecuencias de romper las declaraciones "para ... en". Entonces, la mejor solución es probablemente usar una función como esta, pero no asignarla como un prototipo. Algunas personas pueden argumentar que las declaraciones "for ... in" no deberían usarse para iterar elementos de matriz de todos modos, pero las personas a menudo las usan de esa manera, por lo menos, esta solución debe usarse con precaución.
Code Commander
16
usted debe utilizar siempre for ... incon hasOwnPropertyen cuyo caso el método prototipo está bien
mulllhausen
601

Con Underscore.js o Lo-Dash puedes hacer:

console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

http://underscorejs.org/#union

http://lodash.com/docs#union

GijsjanB
fuente
70
O, quizás incluso mejor que el subrayado, el lodash compatible con API .
Brian M. Hunt
3
@Ygg De los documentos lodash. "Devuelve una nueva matriz de valores únicos, en orden , que están presentes en una o más de las matrices".
Richard Ayotte
44
Prefiero el subrayado.js. Lo que terminé usando es underscore.flatten(), que es mejor que la unión, ya que requiere una gran variedad de matrices.
tejedor
8
@weaver _.flatten se fusiona, pero no se 'deduplica'.
GijsjanB
99
El rendimiento rápido toma lodash vs la respuesta principal: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid
288

Primero concatene las dos matrices, luego filtre solo los elementos únicos:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b)
var d = c.filter((item, pos) => c.indexOf(item) === pos)

console.log(d) // d is [1, 2, 3, 101, 10]

Editar

Como se sugirió, una solución más inteligente sería filtrar los elementos únicos bantes de concatenar con a:

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))

console.log(c) // c is [1, 2, 3, 101, 10]

simo
fuente
55
La solución original aquí tiene el beneficio de eliminar duplicados dentro de cada matriz fuente. Supongo que depende de tu contexto el que usarías.
theGecko
Puede combinar diferentes para IE6-support: c = Array.from (nuevo Set (c));
Tobi G.
Si realmente quiero cambiar apara agregar b, ¿será mejor recorrer y usar push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
temor
1
Solo un recordatorio del uso actual del navegador caniuse.com/usage-table para las personas ansiosas por IE6.
pmrotule
10
@ Andrew: Aún mejor: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy
203

Esta es una solución ECMAScript 6 que utiliza operadores de propagación y genéricos de matriz.

Actualmente solo funciona con Firefox, y posiblemente con Internet Explorer Technical Preview.

Pero si usas Babel , puedes tenerlo ahora.

const input = [
  [1, 2, 3],
  [101, 2, 1, 10],
  [2, 1]
];
const mergeDedupe = (arr) => {
  return [...new Set([].concat(...arr))];
}

console.log('output', mergeDedupe(input));

Adria
fuente
14
Esto debe agregarse a la respuesta aceptada. Esta solución es mucho más eficiente y mucho más elegante de lo que es posible actualmente, pero es lo que inevitablemente podremos hacer (y deberíamos hacer para mantenernos en este campo).
EmmaGamma
Esto no es exactamente lo mismo que la pregunta del OP (esto parece ser más un mapa plano que cualquier otra cosa) sino un voto positivo porque es increíble.
jedd.ahyoung
44
Es difícil decir que esta debería ser la respuesta aceptada, ya que la pregunta es de 2009. Pero sí, esto no solo es más "eficaz" sino también "elegante"
Cezar Augusto
11
Array.fromse puede usar en lugar del operador de propagación: Array.from(new Set([].concat(...arr)))
Henry Blyth
1
Esto es muy elegante Lamentablemente, Typecript no admite esto todavía. stackoverflow.com/questions/33464504/…
Ben Carp
190

ES6

array1.push(...array2) // => don't remove duplication 

O

[...array1,...array2] //   =>  don't remove duplication 

O

[...new Set([...array1 ,...array2])]; //   => remove duplication
Abdennour TOUMI
fuente
1
Primero / segundo ejemplo no es unionen todos + 1er ejemplo golpes hasta la pila para grandes Arrays + tercera ejemplo es increíblemente lento y consume una gran cantidad de memoria, ya que dos intermedios Arrays tienen que ser de construcción + tercera ejemplo, sólo se puede utilizar para unionuna conocida número de Arrays en tiempo de compilación.
Entonces, ¿cómo lo harías?
David Noreña
14
Setes el camino a seguir aquí
philk
3
Tenga en cuenta que for set no puede deduplicar dos objetos que tienen los mismos pares de valores clave a menos que sean las mismas referencias de objeto.
Jun711
2
No funciona con una matriz de objetos, ya que solo fusionará referencias de objetos y no le importa si los objetos mismos son iguales.
Wilson Biggs
87

Usando un Set (ECMAScript 2015), será tan simple como eso:

const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));

Benny Neugebauer
fuente
77
Considero que esta es la "Respuesta aceptada" para usar ES6.
mwieczorek
99
@mwieczorek ¿Qué tal:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen
55
No funciona si está utilizando una matriz de objetos
carkod
1
para combinar diferentes objetos sin duplicar: stackoverflow.com/a/54134237/3131433
Rakibul Haq
38

Aquí hay una versión ligeramente diferente del ciclo. Con algunas de las optimizaciones en la última versión de Chrome, es el método más rápido para resolver la unión de las dos matrices (Chrome 38.0.2111).

http://jsperf.com/merge-two-arrays-keeping-only-unique-values

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [];

var arr = array1.concat(array2),
  len = arr.length;

while (len--) {
  var itm = arr[len];
  if (array3.indexOf(itm) === -1) {
    array3.unshift(itm);
  }
}

ciclo while: ~ 589k ops / s
filtro: ~ 445k ops / s
lodash: 308k ops / s
para bucles: 225k ops / s

Un comentario señaló que una de mis variables de configuración estaba causando que mi ciclo se adelantara al resto, porque no tenía que inicializar una matriz vacía para escribir. Estoy de acuerdo con eso, así que reescribí la prueba para igualar el campo de juego e incluí una opción aún más rápida.

http://jsperf.com/merge-two-arrays-keeping-only-unique-values/52

let whileLoopAlt = function (array1, array2) {
    const array3 = array1.slice(0);
    let len1 = array1.length;
    let len2 = array2.length;
    const assoc = {};

    while (len1--) {
        assoc[array1[len1]] = null;
    }

    while (len2--) {
        let itm = array2[len2];

        if (assoc[itm] === undefined) { // Eliminate the indexOf call
            array3.push(itm);
            assoc[itm] = null;
        }
    }

    return array3;
};

En esta solución alternativa, combiné la solución de matriz asociativa de una respuesta para eliminar la .indexOf()llamada en el ciclo que ralentizaba mucho las cosas con un segundo ciclo, e incluí algunas de las otras optimizaciones que otros usuarios también han sugerido en sus respuestas. .

La respuesta principal aquí con el doble bucle en cada valor (i-1) sigue siendo significativamente más lenta. a lodash todavía le está yendo bien, y todavía se lo recomendaría a cualquiera que no le importe agregar una biblioteca a su proyecto. Para aquellos que no quieren, mi ciclo while sigue siendo una buena respuesta y la respuesta del filtro tiene una muy buena presentación aquí, superando todas mis pruebas con el último Canary Chrome (44.0.2360) a partir de este escrito.

Echa un vistazo a la respuesta de Mike y la respuesta de Dan Stocker si quieres dar un paso a un nivel superior en la velocidad. Esos son, con mucho, el más rápido de todos los resultados después de analizar casi todas las respuestas viables.

slickplaid
fuente
Hay una falla en su metodología: coloca la creación de array3 en la fase de configuración, mientras que ese costo solo debe ser parte de la puntuación de su solución basada en el tiempo. Con esta línea 1 movida , su solución cae a la velocidad del bucle for. Entiendo que la matriz se puede reutilizar, pero tal vez los otros algoritmos también podrían beneficiarse al no tener que declarar e inicializar todos los bloques de construcción necesarios.
doldt
Estoy de acuerdo con su premisa @doldt, pero no estoy de acuerdo con sus resultados. Hay una falla de diseño fundamental con la eliminación de entradas basada en el bucle, ya que debe volver a verificar la longitud de la matriz después de haber eliminado los elementos, lo que resulta en un tiempo de ejecución más lento. Un ciclo while trabajando hacia atrás no tiene estos efectos. Aquí hay un ejemplo con la eliminación de tantas variables de configuración como pueda sin cambiar demasiado su respuesta original: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid
@slickplaid las pruebas vinculadas están vacías, y la próxima revisión en jsperf se bloquea en el ciclo while.
doldt
@doldt Abordé sus inquietudes en mi respuesta y también agregué una prueba actualizada adecuada. Avíseme si está de acuerdo con esos resultados o no. Además, agregué otro mejor resultado usando una matriz asociativa.
slickplaid
1
@slickplaid Gracias por configurar la página de rendimiento extendida. A menos que me falte algo, la función "whileLoopAlt2" no funciona. Crea una nueva matriz que contiene la primera matriz y la segunda matriz (en orden inverso). Para evitar confusiones, hice otra revisión que elimina la función rota. También agregué un ejemplo adicional: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S
37

Puede hacerlo simplemente con ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Utilice el operador de propagación para concatenar la matriz.
  • Use Set para crear un conjunto distinto de elementos.
  • Nuevamente use el operador de propagación para convertir el conjunto en una matriz.
Rajaprabhu Aravindasamy
fuente
2
Recibo un error: el tipo 'Set <string>' no es un tipo de matriz.
gattsbr
3
Y si por alguna razón no desea utilizar el operador de difusión, también hay: Array.from(new Set(array1.concat(array2))).
kba
@gattsbr, con TypeScript en tsconfig.json, puede agregar "downlevelIteration": truea compilerOptions.
VincentPerrin.com
19
Array.prototype.merge = function(/* variable number of arrays */){
    for(var i = 0; i < arguments.length; i++){
        var array = arguments[i];
        for(var j = 0; j < array.length; j++){
            if(this.indexOf(array[j]) === -1) {
                this.push(array[j]);
            }
        }
    }
    return this;
};

Una función de combinación de matriz mucho mejor.

GAgnuevo
fuente
44
var test = ['a', 'b', 'c']; console.log(test); imprimirá ["a", "b", "c", merge: function]
Doubidou
Excelente solucion. He actualizado la prueba jsperf publicada anteriormente por @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) y parece que esta es la más rápida.
Cobra
@Cobra A riesgo de sonar mezquino, ejecutándose en Chrome 40.0.2214 (más reciente a partir del 18/02/15), esta respuesta es un 53% más lenta que la mía. OTOH IE11 parece no estar optimizado para mi respuesta en absoluto. :) Sin embargo, Chrome mobile todavía lo está sacudiendo. Honestamente, si está usando lodash / _ que la mayoría de nosotros debería, la verdadera respuesta ya está bastante arriba en esta lista. :)
slickplaid
@slickplaid Es cierto, y es bastante más rápido, incluso en comparación con el lodash / _ one. Probablemente termine cambiando mi implementación en un momento u otro a algo similar al suyo. : D
Cobra
1
No estoy seguro de cuáles son los costos del método indexOf (), pero este es probablemente el método más rápido compatible con ES5. Tampoco vale nada que la longitud variable de los argumentos no sea necesaria. Este método es encadenable. @slickplaid Cargar una biblioteca nunca es una respuesta a una pregunta de "cómo hacerlo en JavaScript". seguramente muchas bibliotecas tienen una función para hacer este trabajo de 7 líneas.
dehart
19

Solo tirando mis dos centavos.

function mergeStringArrays(a, b){
    var hash = {};
    var ret = [];

    for(var i=0; i < a.length; i++){
        var e = a[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    for(var i=0; i < b.length; i++){
        var e = b[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    return ret;
}

Este es un método que uso mucho, usa un objeto como una tabla hashlookup para hacer la verificación duplicada. Suponiendo que el hash es O (1), esto se ejecuta en O (n) donde n es a.length + b.length. Sinceramente, no tengo idea de cómo el navegador hace el hash, pero funciona bien en muchos miles de puntos de datos.

Miguel
fuente
Muy bien hecho. Supera bastante (si no todos) los otros resultados en esta página al aprovechar la matriz asociativa y evitar el bucle de indexOf y otras operaciones. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
Su "hash" es la String()función en javascript. Lo que podría funcionar para valores primitivos (aunque con colisiones entre tipos), pero no es una buena opción para matrices de objetos.
Bergi
Utilizo una solución similar, permito pasar una función hashCode o pasar una cadena para identificar una propiedad en el objeto para usar como clave hash.
Robert Baker,
19

Simplemente manténgase alejado de los bucles anidados (O (n ^ 2)) y .indexOf()(+ O (n)).

function merge(a, b) {
    var hash = {}, i;
    for (i=0; i<a.length; i++) {
        hash[a[i]]=true;
    } 
    for (i=0; i<b.length; i++) {
        hash[b[i]]=true;
    } 
    return Object.keys(hash);
}
Dan Stocker
fuente
2
Eso es bastante sorprendente, especialmente si estás haciendo cuerdas. Los números necesitarían un paso adicional para mantenerlos como tales. Esta función supera con fuerza todas las demás opciones si no le importa (o no le importa) que todo sea una cadena una vez que haya terminado. Buen trabajo. Resultados de rendimiento aquí: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
18

Simplificó lo mejor de esta respuesta y la convirtió en una buena función:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}
Andrés
fuente
2
Creo que esto es mucho más limpio que la respuesta aceptada. También parece que el filtro es compatible con ECMAScript 5.1 + que ahora es bastante compatible.
Tom Fobear
Esto debería haber sido aceptado
wallop
Esto es mucho más sucinto.
Mox
15

¿Por qué no usas un objeto? Parece que estás tratando de modelar un conjunto. Sin embargo, esto no preservará el orden.

var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true,  "Shakya":true}

// Merge second object into first
function merge(set1, set2){
  for (var key in set2){
    if (set2.hasOwnProperty(key))
      set1[key] = set2[key]
  }
  return set1
}

merge(set1, set2)

// Create set from array
function setify(array){
  var result = {}
  for (var item in array){
    if (array.hasOwnProperty(item))
      result[array[item]] = true
  }
  return result
}
Nick Retallack
fuente
No quiere decir if (!set1.hasOwnProperty(key))?
Gumbo
2
¿Por qué quiero decir eso? El propósito de esa condición es ignorar las propiedades que pueden estar en el prototipo del objeto.
Nick Retallack el
12

La mejor solucion...

Puede verificar directamente en la consola del navegador presionando ...

Sin duplicado

a = [1, 2, 3];
b = [3, 2, 1, "prince"];

a.concat(b.filter(function(el) {
    return a.indexOf(el) === -1;
}));

Con duplicado

["prince", "asish", 5].concat(["ravi", 4])

Si lo desea sin duplicar, puede probar una mejor solución desde aquí: Código de gritos .

[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
    return [1, 2, 3].indexOf(el) === -1;
}));

Probar en la consola del navegador Chrome

 f12 > console

Salida:

["prince", "asish", 5, "ravi", 4]

[1, 2, 3, "prince"]
Zigri2612
fuente
No elimina duplicados de la matriz de salida.
Shahar
9

Mi centavo y medio:

Array.prototype.concat_n_dedupe = function(other_array) {
  return this
    .concat(other_array) // add second
    .reduce(function(uniques, item) { // dedupe all
      if (uniques.indexOf(item) == -1) {
        uniques.push(item);
      }
      return uniques;
    }, []);
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var result = array1.concat_n_dedupe(array2);

console.log(result);
Hero Qu
fuente
No usa nada nuevo en ES6, ¿me perdí algo?
Bergi
@ Bergi: Sí, tienes razón. Gracias por señalar De alguna manera estaba jugando con este script y probablemente había alguna versión con la función ES6, pero ahora contiene indexOf que ha estado allí durante siglos. Mi error, lo siento.
Hero Qu
8

Puede lograrlo simplemente usando Underscore.js's => uniq :

array3 = _.uniq(array1.concat(array2))

console.log(array3)

Imprimirá ["Vijendra", "Singh", "Shakya"] .

Mohideen bin Mohammed
fuente
7

Para ES6, solo una línea:

a = [1, 2, 3, 4]
b = [4, 5]
[...new Set(a.concat(b))]  // [1, 2, 3, 4, 5]
usuario1079877
fuente
7

Sé que esta pregunta no se trata de una variedad de objetos, pero los buscadores terminan aquí.

Por lo tanto, vale la pena agregar para futuros lectores una forma adecuada de combinar ES6 y luego eliminar duplicados

conjunto de objetos :

var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];

var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));

// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]
Stavm
fuente
6
//Array.indexOf was introduced in javascript 1.6 (ECMA-262) 
//We need to implement it explicitly for other browsers, 
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt, from)
  {
    var len = this.length >>> 0;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
//now, on to the problem

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
  if((t = merged.indexOf(i + 1, merged[i])) != -1)
  {
    merged.splice(t, 1);
    i--;//in case of multiple occurrences
  }

La implementación del indexOfmétodo para otros navegadores se toma de MDC

Amarghosh
fuente
1
No pude encontrarlo en w3schools, por eso lo escribí. w3schools.com/jsref/jsref_obj_array.asp ¿Toma un fromparámetro por cierto?
Amarghosh
Gracias @Gumbo y @meder, voy a cambiar mis marcadores ahora. Todavía tengo que hacer algo serio en js y uso w3schools como referencia informal (eso es todo lo que siempre he necesitado), puede ser por eso que no me di cuenta de eso.
Amarghosh
MDC dice que indexOf requiere javascript 1.6 ¿Sería seguro asumir que los navegadores comunes (> = FF2,> IE6, etc.) lo admitirían?
Amarghosh
44
IE6 no es compatible con Array.prototype.indexOf, solo pegue el método de soporte proporcionado por Mozilla para que IE no arroje un error.
meder omuraliev
actualizado usando indexOf. Limpié el código eliminando la parte comentada. @meder: gracias de nuevo.
Amarghosh
6

Nueva solución (que usa Array.prototype.indexOfy Array.prototype.concat):

Array.prototype.uniqueMerge = function( a ) {
    for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
        if ( this.indexOf( a[i] ) === -1 ) {
            nonDuplicates.push( a[i] );
        }
    }
    return this.concat( nonDuplicates )
};

Uso:

>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]

Array.prototype.indexOf (para Internet Explorer):

Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
  {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from): Math.floor(from); 
    if (from < 0)from += len;

    for (; from < len; from++)
    {
      if (from in this && this[from] === elt)return from;
    }
    return -1;
  };
meder omuraliev
fuente
@Mender: si el orden no importa, ¿cómo hago esto
Vijjendra
1
No es un método ECMAScript estándar definido para Array.prototype, aunque sé que puede definirlo fácilmente para IE y otros navegadores que no lo admiten.
meder omuraliev
Tenga en cuenta que este algoritmo es O (n ^ 2).
Gumbo
¿Qué algoritmo es tu respuesta?
meder omuraliev
@meder: mi algoritmo es un algoritmo de unión. La unión en sí se realiza en O (n + m), pero la clasificación toma como máximo O (n · log n + m · log m). Entonces, todo el algoritmo es O (n · log n + m · log m).
Gumbo
6

Se puede hacer usando Set.

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);

//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" > 
  temp text 
</div>

Karan Singla
fuente
6

Hay tantas soluciones para fusionar dos matrices. Se pueden dividir en dos categorías principales (excepto el uso de bibliotecas de terceros como lodash o underscore.js).

a) combine dos matrices y elimine los elementos duplicados.

b) filtrar elementos antes de combinarlos.

Combina dos matrices y elimina elementos duplicados

Combinatorio

// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);

// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2];    // ES6

Unificador

Hay muchas formas de unificar una matriz, personalmente sugiero a continuación dos métodos.

// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];

Filtrar elementos antes de combinarlos

También hay muchas formas, pero personalmente sugiero el siguiente código debido a su simplicidad.

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));
TopW3
fuente
5
Array.prototype.add = function(b){
    var a = this.concat();                // clone current object
    if(!b.push || !b.length) return a;    // if b is not an array, or empty, then return a unchanged
    if(!a.length) return b.concat();      // if original is empty, return b

    // go through all the elements of b
    for(var i = 0; i < b.length; i++){
        // if b's value is not in a, then add it
        if(a.indexOf(b[i]) == -1) a.push(b[i]);
    }
    return a;
}

// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]
Lajos Meszaros
fuente
5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

Lo bueno de este es el rendimiento y que, en general, cuando trabajas con matrices, estás encadenando métodos como filtro, mapa, etc. para que puedas agregar esa línea y concat y deduplicará array2 con array1 sin necesidad de una referencia a lo posterior uno (cuando está encadenando métodos que no tiene), ejemplo:

someSource()
.reduce(...)
.filter(...)
.map(...) 
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc

(No me gusta contaminar Array.prototype y esa sería la única forma de respetar la cadena: definir una nueva función la romperá, así que creo que algo como esto es la única forma de lograrlo)

cancerbero
fuente
4

Puedes probar esto:

const union = (a, b) => Array.from(new Set([...a, ...b]));

console.log(union(["neymar","messi"], ["ronaldo","neymar"]));

mitesh7172
fuente
3

Un enfoque funcional con ES2015

Seguir el enfoque funcional a unionde dos Arrays es solo la composición de concaty filter. Para proporcionar un rendimiento óptimo, recurrimos al Settipo de datos nativo , que está optimizado para búsquedas de propiedades.

De todos modos, la pregunta clave junto con una unionfunción es cómo tratar los duplicados. Son posibles las siguientes permutaciones:

Array A      + Array B

[unique]     + [unique]
[duplicated] + [unique]
[unique]     + [duplicated]
[duplicated] + [duplicated]

Las dos primeras permutaciones son fáciles de manejar con una sola función. Sin embargo, los dos últimos son más complicados, ya que no puede procesarlos mientras confíe en las Setbúsquedas. Dado que cambiar a Objectbúsquedas de propiedades antiguas simples implicaría un golpe de rendimiento serio, la siguiente implementación simplemente ignora la tercera y cuarta permutación. Tendría que construir una versión separada de unionpara apoyarlos.


// small, reusable auxiliary functions

const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// de-duplication

const dedupe = comp(afrom) (createSet);


// the actual union function

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


// mock data

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


// here we go

console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );

De aquí en adelante, resulta trivial implementar una unionnfunción que acepte cualquier número de matrices (inspiradas en los comentarios de naomik):

// small, reusable auxiliary functions

const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

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


// union and unionn

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

const unionn = (head, ...tail) => foldl(union) (head) (tail);


// mock data

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


// here we go

console.log( unionn(xs, ys, zs) );

Resulta que unionnes solo foldl(aka Array.prototype.reduce), que toma unioncomo su reductor. Nota: Dado que la implementación no utiliza un acumulador adicional, arrojará un error cuando lo aplique sin argumentos.


fuente
1
un par de comentarios: me di cuenta de eso flipy notfno se usan. También el unionBypredicado pierde detalles de implementación (requiere conocimiento implícito del Settipo). Sería bueno si pudieras hacer algo como esto: union = unionBy (apply)y unionci = unionBy (p => x => p(x.toLowerCase())). De esa forma, el usuario simplemente envía cualquier valor de agrupación a p- solo una idea ^ _ ^
Gracias
zstambién falta la declaración de variable var/ letpalabra clave
Gracias
1
aquí hay un fragmento de código para aclarar [esencia: unionBy.js ]
Gracias
@naomik Después de repensar mi solución por un tiempo, ya no estoy tan seguro de si es la forma correcta de pasar el predicado. Todo lo que gana es una transformación de cada elemento de la segunda matriz. Me pregunto si este enfoque resuelve más que solo problemas con los juguetes.
3

por el bien de esto ... aquí hay una solución de línea única:

const x = [...new Set([['C', 'B'],['B', 'A']].reduce( (a, e) => a.concat(e), []))].sort()
// ['A', 'B', 'C']

No es particularmente legible, pero puede ayudar a alguien:

  1. Aplica una función de reducción con el valor inicial del acumulador establecido en una matriz vacía.
  2. La función reduce utiliza concat para agregar cada subconjunto al conjunto acumulador.
  3. El resultado de esto se pasa como un parámetro constructor para crear un nuevo Set .
  4. El operador de propagación se utiliza para convertir el Seta una matriz.
  5. La sort()función se aplica a la nueva matriz.
Mark Tyers
fuente
2
También en lugar de reduce()que pueda usarArray.from(set)
Eran Goldin
3

DeDuplicate single o Merge y DeDuplicate múltiples entradas de matriz. Ejemplo a continuación.

utilizando ES6 - Conjunto, para, de desestructuración

Escribí esta función simple que toma múltiples argumentos de matriz. Hace más o menos lo mismo que la solución anterior, solo tiene un caso de uso más práctico. Esta función no concatena valores duplicados en una sola matriz para que pueda eliminarlos en una etapa posterior.

DEFINICIÓN DE FUNCIÓN CORTA (solo 9 líneas)

/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* @params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
   let set = new Set(); // init Set object (available as of ES6)
   for(let arr of args){ // for of loops through values
      arr.map((value) => { // map adds each value to Set object
         set.add(value); // set.add method adds only unique values
      });
   }
   return [...set]; // destructuring set object back to array object
   // alternativly we culd use:  return Array.from(set);
}

EJEMPLO DE USO CODEPEN :

// SCENARIO 
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];

// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);

// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]
DevWL
fuente
¿Por qué usar arr.mapaquí? Lo estás usando como un foreach, ya que el resultado se ignora
Antony
Solía return Array.from (set.values ​​()); , porque vscode da error para return [... set];
makkasi
3
var arr1 = [1, 3, 5, 6];
var arr2 = [3, 6, 10, 11, 12];
arr1.concat(arr2.filter(ele => !arr1.includes(ele)));
console.log(arr1);

output :- [1, 3, 5, 6, 10, 11, 12]
Bharti Ladumor
fuente
3

var array1 = ["one","two"];
var array2 = ["two", "three"];
var collectionOfTwoArrays = [...array1, ...array2];    
var uniqueList = array => [...new Set(array)];
console.log('Collection :');
console.log(collectionOfTwoArrays);    
console.log('Collection without duplicates :');
console.log(uniqueList(collectionOfTwoArrays));

Shiva
fuente