Como mesclar duas matrizes em JavaScript e desduplicar itens

1368

Eu tenho duas matrizes JavaScript:

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

Eu quero que a saída seja:

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

A matriz de saída deve ter palavras repetidas removidas.

Como mesclar duas matrizes em JavaScript para obter apenas os itens exclusivos de cada matriz na mesma ordem em que foram inseridos nas matrizes originais?

Vijjendra
fonte
1
Antes de postar uma nova resposta, considere que já existem mais de 75 respostas para esta pergunta. Por favor, verifique se sua resposta contribui com informações que não estão entre as existentes.
janniks 03/02
[... novo Conjunto ([... [1, 2, 3], ... [2, 3, 4]])] result [1, 2, 3, 4]
Denis Giffeler
Se você deseja uma solução mais genérica que também cubra a fusão profunda, dê uma olhada nesta pergunta . Algumas respostas também abrangem matrizes.
Martin Braun

Respostas:

1667

Para mesclar apenas as matrizes (sem remover duplicatas)

Uso da versão ES5 Array.concat:

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

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

Versão ES6 usa desestruturação

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

Como não existe uma maneira 'incorporada' para remover duplicatas (o ECMA-262 realmente possui o Array.forEachque seria ótimo para isso), precisamos fazer isso 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;
};

Então, para usá-lo:

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

Isso também preservará a ordem das matrizes (ou seja, nenhuma classificação é necessária).

Como muitas pessoas se incomodam com o aumento de protótipos Array.prototypee for inloops, eis uma maneira menos invasiva de usá-lo:

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 aqueles que têm a sorte de trabalhar com navegadores onde o ES5 está disponível, você pode usar o Object.definePropertyseguinte:

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
fonte
281
Observe que esse algoritmo é O (n ^ 2).
Gumbo
7
Let [a, b, c]e [x, b, d]ser as matrizes (assumir aspas). concat dá [a, b, c, x, b, d]. A saída do unique () não seria [a, c, x, b, d]. Isso não preservar a ordem eu acho - eu acredito OP quer[a, b, c, x, d]
Amarghosh
89
O OP aceitou a primeira resposta que o levou a trabalhar e assinou, parece. Ainda estamos comparando soluções uns dos outros, encontrando-n-fixação falhas, melhorando o desempenho, garantindo a sua compatibilidade em todos os lugares e assim por diante ... A beleza de stackoverflow :-)
Amarghosh
6
Eu votei inicialmente para isso, mas mudei de idéia. A atribuição de protótipos ao Array.prototype tem as consequências de quebrar as instruções "for ... in". Portanto, a melhor solução é provavelmente usar uma função como essa, mas não atribuí-la como um protótipo. Algumas pessoas podem argumentar que as declarações "for ... in" não devem ser usadas para iterar os elementos da matriz, mas as pessoas costumam usá-las dessa maneira, pelo menos essa solução deve ser usada com cautela.
Code Commander
16
você deve sempre usar for ... incom hasOwnPropertycaso em que o método protótipo é bom
mulllhausen
600

Com Underscore.js ou Lo-Dash, você pode:

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
fonte
70
Ou, talvez até melhor que o sublinhado, o lodash compatível com API .
Brian M. caça
3
@Ygg A partir dos documentos lodash. "Retorna uma nova matriz de valores exclusivos, em ordem , presentes em uma ou mais das matrizes."
precisa
4
Eu prefiro underscore.js. O que acabei usando é o underscore.flatten()que é melhor que a união, pois é preciso uma matriz de matrizes.
tecelão
8
O @weaver _.flatten é mesclado, mas não 'desduplica'.
GijsjanB
9
Desempenho take rápido em lodash vs a resposta superior: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid
288

Primeiro concatene as duas matrizes, depois filtre apenas os itens exclusivos:

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

Conforme sugerido, uma solução mais inteligente em termos de desempenho seria filtrar os itens exclusivos bantes de concatenar com 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
fonte
5
A solução original aqui tem o benefício de remover dupes em cada matriz de origem. Eu acho que depende do seu contexto que você usaria.
theGecko 26/09
Você pode mesclar diferentes para o suporte ao IE6: c = Array.from (new Set (c));
precisa
Se eu quiser realmente mudar apara adicionar b, será melhor fazer um loop e usar push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
temor
1
Apenas um lembrete do uso atual do navegador caniuse.com/usage-table para pessoas preocupadas com o IE6.
Pmrotule
10
@Andrew: Ainda melhor: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy
203

Esta é uma solução ECMAScript 6 usando operadores de propagação e genéricos de matriz.

Atualmente, ele funciona apenas com o Firefox e, possivelmente, com o Internet Explorer Technical Preview.

Mas se você usa Babel , pode obtê-lo agora.

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
fonte
14
Isso deve ser adicionado à resposta aceita. Essa solução é muito mais eficiente e muito mais elegante do que é possível atualmente, mas é o que inevitavelmente seremos capazes de fazer (e devemos fazer para acompanhar esse campo).
EmmaGamma
Isso não é exatamente o mesmo que a pergunta do OP (isso parece ser mais um mapa plano do que qualquer coisa), mas é positivo porque é incrível.
jedd.ahyoung
4
Difícil dizer que esta deve ser a resposta aceite uma vez que a questão é a partir de 2009. Mas sim, isso não só é mais "performance", mas também "elegante"
Cezar Augusto
11
Array.frompode ser usado em vez de operador de propagação: Array.from(new Set([].concat(...arr)))
Henry Blyth
1
Isso é muito elegante. Infelizmente o Typecript ainda não suporta isso. stackoverflow.com/questions/33464504/…
Ben Carp
190

ES6

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

OU

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

OU

[...new Set([...array1 ,...array2])]; //   => remove duplication
Abdennour TOUMI
fonte
1
1º / 2º exemplo não existe union+ 1º exemplo explode a pilha para Arrays grandes + 3º exemplo é incrivelmente lento e consome muita memória, pois dois Arrays intermediários precisam ser construídos + 3º exemplo só pode ser usado unioncom um conhecido número de Arrays em tempo de compilação.
então como você faria isso?
David Noreña
14
Seté o caminho a percorrer aqui
philk
3
Observe que para set não é possível deduplicar dois objetos que possuem os mesmos pares de valores de chave, a menos que sejam as mesmas referências de objeto.
precisa saber é o seguinte
2
Não funciona com uma matriz de objetos, pois mescla apenas referências a objetos e não se importa se os objetos são iguais.
Wilson Biggs
87

Usando um conjunto (ECMAScript 2015), será tão simples quanto isso:

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

Benny Neugebauer
fonte
7
Considero isso a "Resposta Aceita" para usar o ES6.
precisa saber é o seguinte
9
@mwieczorek Que tal:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen
5
Ele não funciona se você estiver usando uma matriz de objetos
carkod
1
para mesclar objetos diferentes sem duplicar: stackoverflow.com/a/54134237/3131433
Rakibul Haq
38

Aqui está uma visão ligeiramente diferente do loop. Com algumas das otimizações da versão mais recente do Chrome, é o método mais rápido para resolver a união das duas matrizes (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);
  }
}

while loop: ~ 589k ops / s
filter: ~ 445k ops / s
lodash: 308k ops / s
para loops: 225k ops / s

Um comentário apontou que uma das minhas variáveis ​​de instalação estava fazendo com que meu loop se destacasse do restante, porque não era necessário inicializar uma matriz vazia para gravar. Eu concordo com isso, então reescrevi o teste até o campo de jogo e incluí uma opção ainda mais 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;
};

Nesta solução alternativa, eu combinei a solução de matriz associativa de uma resposta para eliminar a .indexOf()chamada no loop, que estava atrasando bastante as coisas com um segundo loop, e incluí algumas das outras otimizações que outros usuários sugeriram em suas respostas também .

A resposta principal aqui com o loop duplo em todos os valores (i-1) ainda é significativamente mais lenta. O lodash ainda está forte, e eu ainda o recomendaria para quem não se importa de adicionar uma biblioteca ao seu projeto. Para aqueles que não querem, meu loop while ainda é uma boa resposta e a resposta do filtro tem uma exibição muito forte aqui, superando todos os meus testes com o mais recente Canary Chrome (44.0.2360) até o momento.

Confira a resposta de Mike e resposta de Dan Stocker se você quiser intensificar-se um entalhe em velocidade. Esses são, de longe, o mais rápido de todos os resultados, depois de passarmos por quase todas as respostas viáveis.

slickplaid
fonte
Existe uma falha na sua metodologia: você coloca a criação do array3 na fase de configuração, enquanto esse custo deve fazer parte apenas da pontuação da sua solução baseada em tempo. Com essa linha 1 movida , sua solução cai para a velocidade da linha for loop. Entendo que a matriz pode ser reutilizada, mas talvez os outros algoritmos também possam se beneficiar por não precisar declarar e inicializar todos os componentes necessários.
doldt
Concordo com sua premissa @doldt, mas discordo de seus resultados. Existe uma falha de design fundamental com a remoção de entradas com base em loop, pois é necessário verificar novamente o comprimento da matriz depois de remover itens, resultando em um tempo de execução mais lento. Um loop while trabalhando para trás não tem esses efeitos. Aqui está um exemplo com a remoção de tantas variáveis de configuração que eu puder, sem alterar a sua resposta original demais: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid
@slickplaid os testes vinculados estão vazios, e a próxima revisão no jsperf fica no loop while.
doldt
@doldt Abordei suas preocupações na minha resposta e adicionei um teste atualizado adequado a ela também. Deixe-me saber se você concorda com esses resultados ou não. Além disso, adicionei outro resultado melhor usando uma matriz associativa.
Slickplaid
1
@slickplaid Obrigado por configurar a página perf estendida. A menos que esteja faltando alguma coisa, a função "whileLoopAlt2" não funciona? Ele cria uma nova matriz contendo a primeira matriz e a segunda matriz (na ordem inversa). Para evitar confusão, fiz outra revisão que remove a função quebrada. I também um exemplo adicional: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S
37

Você pode fazer isso simplesmente com o ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Use o operador de propagação para concatenar a matriz.
  • Use Set para criar um conjunto distinto de elementos.
  • Novamente, use o operador spread para converter o conjunto em uma matriz.
Rajaprabhu Aravindasamy
fonte
2
Eu recebo o erro: O tipo 'Set <string>' não é um tipo de matriz.
21816 Gattsbr # 1434
3
E se por algum motivo não quer usar o operador spread, há também: Array.from(new Set(array1.concat(array2))).
Kb
@gattsbr, com TypeScript tsconfig.json, você pode adicionar "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;
};

Uma função de mesclagem de matriz muito melhor.

GAgnew
fonte
4
var test = ['a', 'b', 'c']; console.log(test); será impresso ["a", "b", "c", merge: function]
Doubidou 4/14
Excelente solução. Atualizei o teste jsperf postado acima por @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) e parece que esse é o mais rápido deles.
Cobra
@Cobra Correndo o risco de parecer mesquinho, rodando no Chrome 40.0.2214 (mais recente em 18/2/15), esta resposta é 53% mais lenta que a minha. OTOH IE11 não parece otimizado para minha resposta. :) No entanto, o Chrome Mobile ainda está balançando. Honestamente, se você estiver usando lodash / _, o que a maioria de nós deveria, a resposta verdadeira já está bem no alto desta lista. :)
slickplaid
@ slickplaid True, e é um pouco mais rápido, mesmo em comparação com o lodash / _ one. Provavelmente, acabarei mudando minha implementação em um ponto ou outro para algo semelhante ao seu. : D
Cobra
1
Não sei ao certo quais são os custos do método indexOf (), mas esse provavelmente é o método mais rápido compatível com ES5. Também não vale nada que o comprimento variável dos argumentos não seja necessário. Este método é encadeado. @slickplaid Carregar uma biblioteca nunca é uma resposta a uma pergunta "como fazê-lo em javascript". certamente muitas bibliotecas têm uma função para realizar esse trabalho de 7 linhas.
DeHart
19

Apenas jogando meus dois 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 é um método que eu uso muito, ele usa um objeto como uma tabela de hashlookup para fazer a verificação duplicada. Supondo que o hash seja O (1), então ele é executado em O (n) onde n é a.length + b.length. Sinceramente, não tenho ideia de como o navegador faz o hash, mas ele tem um bom desempenho em muitos milhares de pontos de dados.

Mike
fonte
Muito bem feito. Supera bastante (se não todos) os outros resultados desta página, aproveitando a matriz associativa e mantendo o loop de indexOf e outras operações. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
Seu "hash" é a String()função em javascript. O que pode funcionar para valores primitivos (embora com colisões entre tipos), mas não é adequado para matrizes de objetos.
Bergi
Uso uma solução semelhante, permito passar uma função hashCode ou passar uma string para identificar uma propriedade no objeto a ser usada como chave de hash.
Robert Baker
19

Apenas evite os loops aninhados (O (n ^ 2)) e .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
fonte
2
Isso é incrível, especialmente se você está fazendo cordas. Os números precisariam de uma etapa adicional para mantê-los como tal. Essa função supera todas as outras opções se você não se importa (ou se importa) de que tudo seja uma string depois que você terminar. Bom trabalho. Resultados de desempenho aqui: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid
18

Simplificou o melhor desta resposta e transformou-a em uma boa função:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}
Andrew
fonte
2
Eu acredito que isso é muito mais limpo do que a resposta aceita. Também parece que o filtro é suportado no ECMAScript 5.1 +, que é bastante suportado agora.
Tom Fobear
Isso deveria ter sido aceito
wallop em
isso é muito mais sucinto.
Mox 22/04
15

Por que você não usa um objeto? Parece que você está tentando modelar um conjunto. Isso não preservará a ordem, no entanto.

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
fonte
Você não quer dizer if (!set1.hasOwnProperty(key))?
Gumbo
2
Por que eu quis dizer isso? O objetivo dessa condição é ignorar propriedades que possam estar no protótipo do objeto.
22410 Nick Retallack
12

A melhor solução...

Você pode verificar diretamente no console do navegador pressionando ...

Sem duplicado

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

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

Com duplicado

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

Se você quiser sem duplicar, pode tentar uma solução melhor a partir daqui - Shouting Code .

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

Experimente no console do navegador Chrome

 f12 > console

Resultado:

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

[1, 2, 3, "prince"]
Zigri2612
fonte
Não remove duplicatas da matriz de saída.
Shahar
9

Meu centavo e meio:

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
fonte
Ele não usa nada de novo no ES6, perdi alguma coisa?
Bergi
@ Bergi: Sim, você está certo. Obrigado por observar. De alguma forma, eu estava brincando com esse script e provavelmente havia alguma versão com a função ES6, mas agora ele contém indexOf, que existe há séculos. Meu erro, desculpe.
herói Qu
8

Você pode consegui-lo simplesmente usando o Underscore.js = => uniq :

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

console.log(array3)

Ele imprimirá ["Vijendra", "Singh", "Shakya"] .

Mohideen bin Mohammed
fonte
7

Para o ES6, apenas uma linha:

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

Eu sei que essa pergunta não é sobre matriz de objetos, mas os pesquisadores acabam aqui.

portanto, vale a pena adicionar aos futuros leitores uma maneira adequada de mesclar ES6 e remover duplicatas

matriz 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
fonte
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
  }

A implementação do indexOfmétodo para outros navegadores é obtida do MDC

Amarghosh
fonte
1
Não consegui encontrá-lo nas escolas w3s, foi por isso que escrevi. w3schools.com/jsref/jsref_obj_array.asp É necessário um fromparâmetro btw?
Amarghosh 18/10/09
Obrigado @Gumbo e @meder - vamos mudar meus favoritos agora. Ainda estou para fazer algo sério em js e uso o w3schools como referência casual (é tudo o que eu sempre precisei) - talvez seja por isso que não percebi isso.
Amarghosh 18/10/09
O MDC diz que indexOf requer javascript 1.6. Seria seguro assumir que os navegadores comuns (> = FF2,> IE6 etc.) o suportariam?
Amarghosh 18/10/09
4
O IE6 não suporta Array.prototype.indexOf, basta colar o método de suporte fornecido pelo Mozilla para que o IE não gere um erro.
meder omuraliev
atualizado usando indexOf. Limpou o código removendo a parte comentada. @ medidor - obrigado novamente.
Amarghosh 18/10/09
6

Nova solução (que usa Array.prototype.indexOfe 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
fonte
@Mender: se a ordem não é matéria, então, como eu faço isso
Vijjendra
1
Não é um método ECMAScript padrão definido para Array.prototype, embora saiba que você pode defini-lo facilmente para o IE e outros navegadores que não o suportam.
meder omuraliev
Observe que esse algoritmo é O (n ^ 2).
Gumbo
Qual algoritmo é a sua resposta?
meder omuraliev
@ medidor: Meu algoritmo é um algoritmo de união. A união em si é feita em O (n + m), mas a classificação leva no máximo O (n · log n + m · log m). Portanto, todo o algoritmo é O (n · log n + m · log m).
Gumbo
6

Isso pode ser feito 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
fonte
6

Existem muitas soluções para mesclar duas matrizes. Eles podem ser divididos em duas categorias principais (exceto o uso de bibliotecas de terceiros, como lodash ou underscore.js).

a) combine duas matrizes e remova itens duplicados.

b) filtre os itens antes de combiná-los.

Combine duas matrizes e remova itens duplicados

Combinando

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

Unificando

Existem várias maneiras de unificar uma matriz, eu pessoalmente sugiro abaixo dois métodos.

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

Filtrar itens antes de combiná-los

Existem também várias maneiras, mas eu pessoalmente sugiro o código abaixo devido à sua simplicidade.

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));
TopW3
fonte
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
fonte
5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

A coisa boa sobre este é o desempenho e que você, em geral, ao trabalhar com matrizes, está encadeando métodos como filtro, mapa, etc. um (quando você está encadeando métodos que não possui), exemplo:

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

(Não gosto de poluir o Array.prototype e essa seria a única maneira de respeitar a cadeia - definir uma nova função a interromperá -, então acho que algo assim é a única maneira de conseguir isso)

cancerbero
fonte
4

Você pode tentar isso:

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

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

mitesh7172
fonte
3

Uma abordagem funcional com o ES2015

Seguindo a abordagem funcional, a unionde dois Arrays é apenas a composição de concate filter. Para fornecer um desempenho ideal, recorremos ao Settipo de dados nativo , que é otimizado para pesquisas de propriedades.

De qualquer forma, a questão principal em conjunto com uma unionfunção é como tratar duplicatas. As seguintes permutações são possíveis:

Array A      + Array B

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

As duas primeiras permutações são fáceis de manusear com uma única função. No entanto, os dois últimos são mais complicados, pois você não pode processá-los enquanto confiar nas Setpesquisas. Como a mudança para Objectpesquisas simples de propriedades antigas implicaria um sério desempenho, a implementação a seguir ignora a terceira e quarta permutação. Você precisaria criar uma versão separada unionpara suportá-los.


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

A partir daqui, torna-se trivial implementar uma unionnfunção que aceita qualquer número de matrizes (inspiradas nos comentários 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) );

Acontece que unionné apenas foldl(aka Array.prototype.reduce), que assume unioncomo seu redutor. Nota: Como a implementação não usa um acumulador adicional, gera um erro quando você a aplica sem argumentos.


fonte
1
alguns feedbacks: notei isso flipe notfnão são utilizados. Também unionBypredicam detalhes de implementação de vazamentos (requer conhecimento implícito do Settipo). Pode ser bom se você pudesse fazer algo assim: union = unionBy (apply)e unionci = unionBy (p => x => p(x.toLowerCase())). Dessa forma, o usuário envia apenas o valor do agrupamento p- apenas uma idéia ^ _ ^
Obrigado
zsdeclaração de variável também não possui var/ letpalavra
Obrigado
1
aqui está um trecho de código para esclarecer [gist: unionBy.js ]
Obrigado
@naomik Depois de repensar minha solução por um tempo, não tenho mais tanta certeza se é o caminho certo para passar o predicado. Tudo que você ganha é uma transformação de cada elemento da segunda matriz. Gostaria de saber se essa abordagem resolve mais do que apenas problemas com brinquedos.
3

por uma questão de ... aqui está uma solução de linha única:

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

Não é particularmente legível, mas pode ajudar alguém:

  1. Aplica uma função de redução com o valor inicial do acumulador definido como uma matriz vazia.
  2. A função reduzir usa concat para anexar cada sub-matriz na matriz do acumulador.
  3. O resultado disso é passado como um parâmetro construtor para criar um novo Set .
  4. O operador de propagação é usado para converter o arquivo Setem uma matriz.
  5. A sort()função é aplicada à nova matriz.
Mark Tyers
fonte
2
Também em vez de reduce()você pode usarArray.from(set)
Eran Goldin
3

Desduplicar entradas simples ou Mesclar e Desduplicar várias matrizes. Exemplo abaixo.

usando ES6 - Defina, para, desestruturação

Eu escrevi essa função simples que recebe vários argumentos de matriz. Faz praticamente o mesmo que a solução acima, apenas possui casos de uso mais práticos. Essa função não concatena valores duplicados em uma matriz apenas para que possa excluí-los em algum estágio posterior.

DEFINIÇÃO DE CURTA FUNÇÃO (apenas 9 linhas)

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

EXEMPLO 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
fonte
Por que usar arr.mapaqui? Você está usando-o como um foreach, pois o resultado é ignorado
Antony
Eu usei return Array.from (set.values ​​()); , porque vscode dá erro para retornar [... set];
makkasi 4/09/19
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
fonte
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
fonte