Como juntar duas matrizes em JavaScript e des-duplicate itens
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
quero que a saída seja:
var array3 = ["Vijendra","Singh","Shakya"];
a matriz de saída deve ter as palavras repetidas removidas.
Como posso juntar duas matrizes em JavaScript de modo a obter apenas os itens únicos de cada matriz na mesma ordem em que foram inseridos nas matrizes originais?
30 answers
Basta juntar as matrizes (sem remover duplicados)
Uso da versão ES5 Array.concat
:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2); // Merges both arrays
// [ 'Vijendra', 'Singh', 'Singh', 'Shakya' ]
Versão ES6 use destruição
const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];
Uma vez que não existe uma forma 'construída' de remover duplicados (o ECMA-262 tem realmente Array.forEach
o que seria óptimo para isto), temos de o fazer 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();
Isto também preservará a ordem das matrizes (ou seja, nenhuma ordenação necessária).
Desde muitos as pessoas estão irritadas com o aumento do protótipo de loops Array.prototype
e for in
, Aqui está 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 Object.defineProperty
assim:
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;
}
});
Com Sublinhado.js ou Lo-Dash você pode fazer:
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
Primeiro concatenar as duas matrizes, a seguir filtrar apenas os itens únicos.
var a = [1, 2, 3], b = [101, 2, 1, 10];
var c = a.concat(b);
var d = c.filter(function (item, pos) {return c.indexOf(item) == pos});
// d is [1,2,3,101,10]
Http://jsfiddle.net/simo/98622/
Editar
Como sugerido por @Dmitry (veja o segundo comentário abaixo) uma solução mais eficiente seria filtrar os itens únicos em b
antes de concatenar com a
var a = [1, 2, 3], b = [101, 2, 1, 10];
var c = a.concat(b.filter(function (item) {
return a.indexOf(item) < 0;
}));
// d is [1,2,3,101,10]
Esta é uma solução ECMAScript 6 Usando o operador de expansão e os genéricos de matriz.
Actualmente, só funciona com o Firefox e, possivelmente, com o Internet Explorer Technical Preview.Mas se usares Babel , podes ficar com ele agora.
// Input: [ [1, 2, 3], [101, 2, 1, 10], [2, 1] ]
// Output: [1, 2, 3, 101, 10]
function mergeDedupe(arr)
{
return [...new Set([].concat(...arr))];
}
ES6
array1.push(...array2) // => don't remove duplication
Ou
[...array1,...array2] // => don't remove duplication
Ou
[...new Set([...array1 ,...array2])]; // => remove duplication
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
filtro: ~445k ops / s
lodash: 308k ops / s
para loops: 225k ops / s
Http://jsperf.com/merge-two-arrays-keeping-only-unique-values/21
var whileLoopAlt = function(array1, array2) {
var array3 = [];
var arr = array1.concat(array2);
var len = arr.length;
var assoc = {};
while(len--) {
var itm = arr[len];
if(!assoc[itm]) { // Eliminate the indexOf call
array3.unshift(itm);
assoc[itm] = true;
}
}
return array3;
};
Nesta solução alternativa, combinei a solução associativa de uma resposta para eliminar a chamada no loop que estava a atrasar muito as coisas com um segundo loop, e incluiu algumas das outras otimizações que outros usuários têm sugerido em suas respostas também.
A resposta superior aqui com o duplo laço em cada valor (i-1) ainda é significativamente mais lenta. lodash ainda está fazendo forte, e eu ainda recomendo para qualquer um que não se importa de adicionar uma biblioteca para o seu projeto. Para aqueles que não querem, o meu while loop ainda é uma boa resposta e a resposta do filtro tem uma exibição muito forte aqui, batendo em todos os meus testes com o mais recente Canário Cromado (44.0.2360) a partir desta escrita.
Vê a resposta do Mikee a resposta do Dan Stocker {[[8]}Se quiseres aumentar um pouco a velocidade. Estes são de longe o mais rápido de todos os resultados depois de passar por quase todas as respostas viáveis.
Podes fazê-lo simplesmente com ECMAScript 6,
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
- usar o operador de spread para concatenar a matriz.
- usar O conjunto para criar um conjunto distinto de elementos.
- novamente use o operador de spread para converter o conjunto em um array.
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 junção de array muito melhor.
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 hashlookup para fazer a verificação duplicada. Assumindo que o hash é O(1), então isso corre em O(n) onde n é a.comprimento + B. comprimento. Eu honestamente Não tenho idéia de como o navegador faz o hash, mas ele funciona bem em muitos milhares de pontos de dados.
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
}
Mantém-te afastado dos laços 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);
}
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);
Simplificoua resposta do simo e transformou-a numa boa função.
function mergeUnique(arr1, arr2){
return arr1.concat(arr2.filter(function (item) {
return arr1.indexOf(item) === -1;
}));
}
A melhor solução...
Você pode verificar directamente na consola do navegador, batendo...
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 quiser sem duplicado, pode tentar uma solução melhor daqui - código de gritaria .
[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
return [1, 2, 3].indexOf(el) === -1;
}));
Tente na consola do navegador Chrome
f12 > console
Resultado:
["prince", "asish", 5, "ravi", 4]
[1, 2, 3, "prince"]
//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 método indexOf
para outros navegadores é retirada de MDC
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>
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]
Podes alcançá-lo apenas com o Underscore.js => uniq:
array3 = _.uniq(array1.concat(array2))
console.log(array3)
Vai imprimir ["Vijendra"," Singh", "Shakya"] .
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
A coisa boa sobre isso é desempenho e que você, em geral, quando se trabalha com matrizes, são o encadeamento de métodos como filtro de mapa, etc, então você pode adicionar a linha e vai concat e desduplicar matriz2 com matriz1 sem a necessidade de uma referência para a tarde (quando você está encadeamento de métodos, você não tem), 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 que seria a única maneira de respeitar a cadeia-definir uma nova função vai quebrá-lo - por isso eu pense que algo assim é a única maneira de conseguir isso)
Nova solução ( que utiliza Array.prototype.indexOf
e 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 )
};
Utilização:
>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]
Matriz.prototipo.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;
};
No Dojo 1, 6+
var unique = [];
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2); // Merged both arrays
dojo.forEach(array3, function(item) {
if (dojo.indexOf(unique, item) > -1) return;
unique.push(item);
});
Actualizar
Ver código de trabalho.
Juntar um número ilimitado de arrays ou não-arrays e mantê-lo único:
function flatMerge() {
return Array.prototype.reduce.call(arguments, function (result, current) {
if (!(current instanceof Array)) {
if (result.indexOf(current) === -1) {
result.push(current);
}
} else {
current.forEach(function (value) {
console.log(value);
if (result.indexOf(value) === -1) {
result.push(value);
}
});
}
return result;
}, []);
}
flatMerge([1,2,3], 4, 4, [3, 2, 1, 5], [7, 6, 8, 9], 5, [4], 2, [3, 2, 5]);
// [1, 2, 3, 4, 5, 7, 6, 8, 9]
flatMerge([1,2,3], [3, 2, 1, 5], [7, 6, 8, 9]);
// [1, 2, 3, 5, 7, 6, 8, 9]
flatMerge(1, 3, 5, 7);
// [1, 3, 5, 7]
Assumindo que as matrizes originais não precisam de Des-duplicação, isto deve ser muito rápido, manter a ordem original, e não modifica as matrizes originais...
function arrayMerge(base, addendum){
var out = [].concat(base);
for(var i=0,len=addendum.length;i<len;i++){
if(base.indexOf(addendum[i])<0){
out.push(addendum[i]);
}
}
return out;
}
Utilização:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = arrayMerge(array1, array2);
console.log(array3);
//-> [ 'Vijendra', 'Singh', 'Shakya' ]
Seguindo a abordagem funcional a union
de dois Array
é apenas a composição de concat
e filter
. A fim de fornecer o desempenho ideal, nós usamos o tipo de dados native Set
, que é otimizado para a pesquisa de propriedades.
De qualquer forma, a questão chave em conjunto com uma função union
é como tratar duplicados. São possíveis as seguintes permutações:
Array A + Array B
[unique] + [unique]
[duplicated] + [unique]
[unique] + [duplicated]
[duplicated] + [duplicated]
As duas primeiras permutações são fáceis de lidar com funcao. No entanto, os dois últimos são mais complicados, uma vez que você não pode processá-los, desde que você confie em pesquisas Set
. Uma vez que mudar para plain old Object
a pesquisa de propriedades implicaria um desempenho grave, a implementação seguinte ignora apenas a terceira e quarta permutação. Você teria que construir uma versão separada de union
para apoiá-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) );
unionn
função, que aceita qualquer número de arrays (inspirado 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
(Também conhecido por Array.prototype.reduce
), que toma union
como redutor. Nota: uma vez que a implementação não usa um acumulador adicional, ele irá lançar um erro quando você aplicá-lo sem argumentos.
A maneira mais fácil de fazer isto é usar concat()
para juntar as matrizes e depois usar filter()
Para remover os duplicados, ou para usar concat()
e depois colocar a matriz reunida dentro de um Set()
.
Primeira via:
const firstArray = [1,2, 2];
const secondArray = [3,4];
// now lets merge them
const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4]
//now use filter to remove dups
const removeDuplicates = mergedArray.filter((elem, index) => mergedArray.indexOf(elem) === index); // [1,2,3, 4]
Segunda Via (mas com implicações de desempenho na IU):
const firstArray = [1,2, 2];
const secondArray = [3,4];
// now lets merge them
const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4]
const removeDuplicates = new Set(mergedArray);
Nota: estou a fundir 2 matrizes de objectos por chave
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<button type='button' onclick='doit()'>do it</button>
<script>
function doit(){
var items = [];
var items2 = [];
var itemskeys = {};
for(var i = 0; i < 10000; i++){
items.push({K:i, C:"123"});
itemskeys[i] = i;
}
for(var i = 9000; i < 11000; i++){
items2.push({K:i, C:"123"});
}
console.time('merge');
var res = items.slice(0);
//method1();
method0();
//method2();
console.log(res.length);
console.timeEnd('merge');
function method0(){
for(var i = 0; i < items2.length; i++){
var isok = 1;
var k = items2[i].K;
if(itemskeys[k] == null){
itemskeys[i] = res.length;
res.push(items2[i]);
}
}
}
function method1(){
for(var i = 0; i < items2.length; i++){
var isok = 1;
var k = items2[i].K;
for(var j = 0; j < items.length; j++){
if(items[j].K == k){
isok = 0;
break;
}
}
if(isok) res.push(items2[i]);
}
}
function method2(){
res = res.concat(items2);
for(var i = 0; i < res.length; ++i) {
for(var j = i+1; j < res.length; ++j) {
if(res[i].K === res[j].K)
res.splice(j--, 1);
}
}
}
}
</script>
</body>
</html>
Por causa disso... 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.
- aplica uma função de redução com o valor inicial do acumulador configurado numa matriz vazia.
- a função reduce usa o concat para adicionar cada sub-array ao array acumulador.
- o resultado disto é passado como um parâmetro construtor para criar um novo
Set
. - o operador de spread é utilizado para converter o
Set
para um matriz. - a função
sort()
é aplicada à nova matriz.
Deduplicato simples ou de junção e Deduplicato de entradas múltiplas de array. Exemplo abaixo.
Useing ES6-Set, for of, destructing {[[6]}
Eu escrevi esta função simples que leva vários argumentos de array. Faz praticamente o mesmo que a solução acima dele apenas tem um caso de uso mais prático. Esta função não concatenate duplicate valores em um único array apenas para que ele possa excluí-los em algum estágio posterior.
Definição de função curta (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);
}
USAR O EXEMPLO CODEPENO:
// 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]
Juntar duas matrizes e remover duplicados no es6
let arr1 = [3, 5, 2, 2, 5, 5];
let arr2 = [2, 1, 66, 5];
let unique = [...new Set([...arr1,...arr2])];
console.log(unique);
// [ 3, 5, 2, 1, 66 ]