Come unire due matrici in JavaScript e de-duplicare gli elementi


1368

Ho due matrici JavaScript:

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

Voglio che l'output sia:

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

L'array di output dovrebbe avere parole ripetute rimosse.

Come unire due array in JavaScript in modo da ottenere solo gli elementi univoci da ciascun array nello stesso ordine in cui sono stati inseriti negli array originali?


1
Prima di pubblicare una nuova risposta, considera che ci sono già più di 75 risposte per questa domanda. Assicurati che la tua risposta fornisca informazioni che non sono tra le risposte esistenti.
Janniks,

[... new Set ([... [1, 2, 3], ... [2, 3, 4]])] result [1, 2, 3, 4]
Denis Giffeler

Se desideri una soluzione più generica che copra anche la fusione profonda, dai un'occhiata a questa domanda . Alcune risposte riguardano anche gli array.
Martin Braun,

Risposte:


1667

Per unire le matrici (senza rimuovere i duplicati)

Uso della versione ES5 Array.concat:

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

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

La versione ES6 usa la destrutturazione

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

Dal momento che non esiste un modo "integrato" per rimuovere i duplicati ( ECMA-262 ha effettivamente Array.forEachquale sarebbe fantastico per questo), dobbiamo farlo 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;
};

Quindi, per usarlo:

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

Ciò preserverà anche l'ordine delle matrici (ovvero, non è necessario alcun ordinamento).

Poiché molte persone sono infastidite dal potenziamento Array.prototypee dai for inloop dei prototipi , ecco un modo meno invasivo per 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));

Per coloro che sono abbastanza fortunati da lavorare con i browser in cui è disponibile ES5, è possibile utilizzare in Object.definePropertyquesto modo:

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

281
Si noti che questo algoritmo è O (n ^ 2).
Gumbo,

7
Lasciate che [a, b, c]e [x, b, d]sia gli array (assumere le virgolette). concat dà [a, b, c, x, b, d]. L'output di unique () non sarebbe [a, c, x, b, d]. Ciò non preserva l'ordine che penso - credo che OP voglia[a, b, c, x, d]
Amarghosh,

89
OP ha accettato la prima risposta che lo ha fatto lavorare e ha firmato sembra. Stiamo ancora confrontando le soluzioni degli altri, trovando i guasti e risolvendo i problemi, migliorandone le prestazioni, assicurandoci che sia compatibile ovunque e così via ... La bellezza di StackOverflow :-)
Amarghosh,

6
Inizialmente ho votato a favore, ma ho cambiato idea. L'assegnazione di prototipi ad Array.prototype ha le conseguenze della rottura delle dichiarazioni "for ... in". Quindi la soluzione migliore è probabilmente quella di utilizzare una funzione come questa ma non assegnarla come prototipo. Alcune persone potrebbero obiettare che le dichiarazioni "for ... in" non dovrebbero essere usate per iterare comunque gli elementi dell'array, ma le persone spesso li usano in questo modo, perlomeno questa soluzione deve essere usata con cautela.
Codice comandante

16
si dovrebbe sempre usare for ... incon hasOwnProperty, nel qual caso il metodo prototipo è bene
mulllhausen

600

Con Underscore.js o Lo-Dash puoi fare:

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


70
O, forse anche meglio di sottolineare, il lodash compatibile con API .
Brian M. Hunt,

3
@Ygg Dai documenti lodash. "Restituisce un nuovo array di valori univoci, in ordine , presenti in uno o più array."
Richard Ayotte,

4
Preferisco underscore.js. Quello che ho finito per usare è underscore.flatten(), che è meglio dell'unione in quanto richiede una serie di array.
tessitore

8
@weaver _.flatten si unisce, ma non "de-duplica".
GijsjanB,

9
Esibizione rapida delle prestazioni su lodash contro la risposta migliore: jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid

288

Prima concatenare i due array, quindi filtrare solo gli elementi unici:

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]

modificare

Come suggerito, una soluzione più saggia in termini di prestazioni sarebbe quella di filtrare gli elementi unici bprima di concatenarli 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]


5
La soluzione originale qui ha il vantaggio di rimuovere i duplicati all'interno di ciascun array di origine. Immagino che dipenda dal contesto che useresti.
theGecko,

È possibile unire diversi per il supporto IE6: c = Array.from (nuovo set (c));
Tobi G.,

Se voglio davvero cambiare aper aggiungere b, sarà meglio passare in rassegna e usare push? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
timore re

1
Solo un promemoria dell'attuale utilizzo del browser caniuse.com/usage-table per le persone in ansia per IE6.
pmrotule,

10
@Andrew: Ancora meglio: 1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy

203

Questa è una soluzione ECMAScript 6 che utilizza operatore di diffusione e generici di array.

Attualmente funziona solo con Firefox e probabilmente con l'anteprima tecnica di Internet Explorer.

Ma se usi Babel , puoi averlo ora.

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

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


14
Questo dovrebbe essere aggiunto alla risposta accettata. Questa soluzione è molto più efficiente e molto più elegante di quanto sia attualmente possibile, ma è ciò che inevitabilmente saremo in grado di fare (e dovremmo fare per stare al passo in questo campo).
EmmaGamma,

Questa non è la stessa cosa della domanda del PO (questa sembra essere più di una flatmap di ogni altra cosa) ma va votata perché è fantastica.
jedd.ahyoung,

4
Difficile dire che questa dovrebbe essere la risposta accettata poiché la domanda è del 2009. Ma sì, questo non solo è più "performante" ma anche "elegante"
Cezar Augusto,

11
Array.frompuò essere utilizzato al posto dell'operatore spread: Array.from(new Set([].concat(...arr)))
Henry Blyth,

1
Questo è molto elegante. Purtroppo Typescript non supporta ancora questo. 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

1
Il 1 ° / 2 ° esempio non è unionaffatto + 1 ° esempio fa esplodere lo stack per grandi Arrays + 3 ° esempio è incredibilmente lento e consuma molta memoria, poiché Arraydevono essere costruiti due s intermedi + 3 ° esempio può essere usato solo unioncon un noto numero di messaggi Arrayal momento della compilazione.

quindi come lo faresti?
David Noreña,

14
Setè la strada da percorrere qui
philk

3
Si noti che per set non è possibile deduplicare due oggetti che hanno le stesse coppie di valori chiave a meno che non siano gli stessi riferimenti a oggetti.
Jun711,

2
Non funziona con una matrice di oggetti, in quanto unirà solo i riferimenti agli oggetti e non importa se gli oggetti stessi sono uguali.
Wilson Biggs

87

Usando un Set (ECMAScript 2015), sarà semplice quanto segue:

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


7
Considero questa la "risposta accettata" per l'utilizzo di ES6.
mwieczorek,

9
@mwieczorek Che ne dici di:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen,

5
Non funziona se si utilizza una matrice di oggetti
carkod,

1
per unire diversi oggetti senza duplicare:
stackoverflow.com/a/54134237/3131433

38

Ecco una versione leggermente diversa del ciclo. Con alcune delle ottimizzazioni dell'ultima versione di Chrome, è il metodo più veloce per risolvere l'unione dei due array (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
filtro: ~ 445k ops / s
lodash: 308k ops / s
per loop: 225k ops / s

Un commento ha sottolineato che una delle mie variabili di installazione stava facendo sì che il mio ciclo venisse portato avanti rispetto al resto, perché non era necessario inizializzare un array vuoto su cui scrivere. Sono d'accordo, quindi ho riscritto il test anche sul campo di gioco e ho incluso un'opzione ancora più veloce.

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

In questa soluzione alternativa, ho combinato la soluzione di array associativo di una risposta per eliminare il file .indexOf() chiamata nel loop che rallentava molto le cose con un secondo loop e includevo anche alcune delle altre ottimizzazioni suggerite da altri utenti nelle loro risposte .

La risposta migliore qui con il doppio loop su ogni valore (i-1) è ancora significativamente più lenta. lodash sta ancora andando forte e lo consiglierei ancora a chiunque non si preoccupi di aggiungere una biblioteca al suo progetto. Per coloro che non vogliono, il mio ciclo while è ancora una buona risposta e la risposta del filtro ha una forte dimostrazione qui, battendo tutti i miei test con l'ultimo Canary Chrome (44.0.2360) al momento in cui scrivo.

Scopri la risposta di Mike e la risposta di Dan Stocker se si vuole fare un passo su una tacca in termini di velocità. Questi sono di gran lunga il più veloce di tutti i risultati dopo aver esaminato quasi tutte le risposte possibili.


C'è un difetto nella tua metodologia: hai messo la creazione di array3 nella fase di installazione, mentre quel costo dovrebbe essere solo parte del punteggio della tua soluzione while-based. Con questa linea 1 spostata , la soluzione scende alla velocità di quella basata su loop. Capisco che l'array può essere riutilizzato, ma forse anche gli altri algoritmi potrebbero trarre vantaggio dal fatto di non dover dichiarare e inizializzare ogni blocco predefinito necessario.
Doldt

Sono d'accordo con la tua premessa @doldt, ma non sono d'accordo con i tuoi risultati. Esiste un difetto di progettazione fondamentale nella rimozione delle voci basata su loop, in quanto è necessario ricontrollare la lunghezza dell'array dopo aver rimosso gli elementi, con un conseguente rallentamento dei tempi di esecuzione. Un ciclo while che lavora all'indietro non ha questi effetti. Ecco un esempio con la rimozione di quante più variabili di impostazione possibile senza modificare troppo la loro risposta originale: jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid

@slickplaid i test collegati sono vuoti e la prossima revisione su jsperf si blocca nel ciclo while.
Doldt

@doldt Ho risposto alle tue preoccupazioni nella mia risposta e ho aggiunto anche un test aggiornato adeguato. Fammi sapere se sei d'accordo con questi risultati o meno. Inoltre, ho aggiunto un altro risultato migliore usando una matrice associativa.
slickplaid

1
@slickplaid Grazie per aver impostato la pagina perf estesa. A meno che non mi manchi qualcosa, la funzione "whileLoopAlt2" non funziona? Crea un nuovo array contenente il primo array e il secondo array (in ordine inverso). Per evitare confusione ho fatto un'altra revisione che rimuove la funzione non funzionante. Ho anche aggiunto un altro esempio: jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S

37

Puoi farlo semplicemente con ECMAScript 6,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • Utilizzare l' operatore spread per concatenare l'array.
  • Utilizzare Set per creare un set distinto di elementi.
  • Usa nuovamente l'operatore spread per convertire il Set in un array.

2
Ottengo l'errore: digitare 'Set <string>' non è un tipo di array.
gattsbr,

3
E se per qualche motivo non si vuole utilizzare l'operatore diffusione, c'è anche: Array.from(new Set(array1.concat(array2))).
kba,

@gattsbr, con TypeScript in tsconfig.json, è possibile aggiungere "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 funzione di unione di array molto migliore.


4
var test = ['a', 'b', 'c']; console.log(test); stamperà ["a", "b", "c", merge: function]
Doubidou

Soluzione eccellente Ho aggiornato il test jsperf pubblicato sopra da @slickplaid ( jsperf.com/merge-two-arrays-keeping-only-unique-values/3 ) e sembra che questo sia il più veloce.
Cobra,

@Cobra A rischio di sembrare insignificante, in esecuzione su Chrome 40.0.2214 (ultimo il 18/02/15), questa risposta è del 53% più lenta della mia. OTOH IE11 non sembra affatto ottimizzato per la mia risposta. :) Chrome mobile lo sta ancora scuotendo, però. Onestamente, se stai usando lodash / _ che la maggior parte di noi dovrebbe, la vera risposta è già abbastanza in alto in questo elenco. :)
slickplaid

@slickplaid True, ed è un po 'più veloce, anche rispetto a lodash / _ one. Probabilmente finirò per cambiare la mia implementazione in un punto o nell'altro in qualcosa di simile al tuo. : D
Cobra,

1
Non sono sicuro di quali siano i costi del metodo indexOf () ma questo è probabilmente il metodo compatibile ES5 più veloce. Inoltre, non vale nulla che la lunghezza variabile degli argomenti non sia necessaria. Questo metodo è concatenabile. @slickplaid Il caricamento di una libreria non è mai una risposta a una domanda "come farlo in javascript". sicuramente molte librerie hanno una funzione per fare questo lavoro a 7 righe.
Dehart,

19

Sto solo gettando i miei due centesimi.

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

Questo è un metodo che uso molto, utilizza un oggetto come tabella hashlookup per eseguire il controllo duplicato. Supponendo che l'hash sia O (1), allora questo corre in O (n) dove n è a.length + b.length. Onestamente non ho idea di come il browser esegua l'hash, ma funziona bene su molte migliaia di punti dati.


Molto ben fatto. Espone abbastanza (se non tutti) gli altri risultati in questa pagina sfruttando l'array associativo ed evitando il looping di indexOf e di altre operazioni. jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

Il tuo "hash" è la String()funzione in javascript. Che potrebbe funzionare per valori primitivi (anche se con collisioni tra tipi), ma non è adatto per matrici di oggetti.
Bergi,

Uso una soluzione simile, consento il passaggio di una funzione hashCode o il passaggio di una stringa per identificare una proprietà nell'oggetto da utilizzare come chiave hash.
Robert Baker,

19

Evita gli anelli nidificati (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);
}

2
È piuttosto sorprendente, specialmente se stai facendo delle corde. I numeri avrebbero bisogno di un ulteriore passaggio per mantenerli come tali. Questa funzione batte pesantemente tutte le altre opzioni se non ti dispiace (o ti interessa) che tutto sia una stringa dopo aver finito. Bel lavoro. Risultati delle prestazioni qui: jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid

18

Meglio semplificato di questa risposta e trasformato in una bella funzione:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}

2
Credo che sia molto più pulito della risposta accettata. Inoltre sembra che il filtro sia supportato in ECMAScript 5.1 +, che ora è abbastanza supportato.
Tom Fobear,

Questo avrebbe dovuto essere accettato
wallop il

questo è molto più succinto.
Mox,

15

Perché non usi un oggetto? Sembra che tu stia cercando di modellare un set. Questo non preserverà l'ordine, tuttavia.

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
}

Non intendi if (!set1.hasOwnProperty(key))?
Gumbo,

2
Perché dovrei dire questo? Lo scopo di tale condizione è ignorare le proprietà che potrebbero essere nel prototipo dell'oggetto.
Nick Retallack,

12

La migliore soluzione...

Puoi controllare direttamente nella console del browser premendo ...

Senza duplicati

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

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

Con duplicato

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

Se vuoi senza duplicati, puoi provare una soluzione migliore da qui: Gridare Codice .

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

Prova sulla console del browser Chrome

 f12 > console

Produzione:

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

[1, 2, 3, "prince"]

Non rimuove i duplicati dall'array di output.
Shahar,

9

Il mio centesimo e mezzo:

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

Non usa nulla di nuovo in ES6, mi sono perso qualcosa?
Bergi,

@Bergi: Sì, hai ragione. Grazie per averlo notato. In qualche modo stavo giocando con questo script e probabilmente c'era una versione con la funzione ES6, ma ora contiene indexOf che è lì da secoli. Il mio errore, scusa.
Hero Qu

8

Puoi ottenerlo semplicemente usando Underscore.js = = uniq :

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

console.log(array3)

Stampa ["Vijendra", "Singh", "Shakya"] .


7

Per ES6, solo una riga:

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

7

So che questa domanda non riguarda l'array di oggetti, ma i ricercatori finiscono qui.

quindi vale la pena aggiungere per i futuri lettori un modo ES6 adeguato di unire e quindi rimuovere i duplicati

matrice di oggetti :

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

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
  }

L'implementazione del indexOfmetodo per altri browser è presa da MDC


1
Non riuscivo a trovarlo in w3schools, ecco perché l'ho scritto. w3schools.com/jsref/jsref_obj_array.asp Prende un fromparametro tra l'altro?
Amarghosh,

Grazie @Gumbo e @meder - adesso cambierò i miei segnalibri. Devo ancora fare qualcosa di serio in js e uso w3schools come riferimento casuale (è tutto ciò di cui ho mai avuto bisogno) - forse è per questo che non me ne sono reso conto.
Amarghosh,

MDC dice che indexOf richiede javascript 1.6 Sarebbe sicuro supporre che i browser comuni (> = FF2,> IE6 ecc.) Lo supporteranno?
Amarghosh,

4
IE6 non supporta Array.prototype.indexOf, basta incollare il metodo di supporto fornito da Mozilla in modo che IE non generi un errore.
meder omuraliev,

aggiornato utilizzando indexOf. Pulito il codice rimuovendo la parte commentata. @meder - grazie ancora.
Amarghosh,

6

Nuova soluzione (che utilizza 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 (per 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;
  };

@Mender: se l'ordine non ha importanza, allora come lo faccio
Vijjendra,

1
Non è un metodo ECMAScript standard definito per Array.prototype, anche se sono consapevole che puoi facilmente definirlo per IE e altri browser che non lo supportano.
meder omuraliev,

Si noti che questo algoritmo è O (n ^ 2).
Gumbo,

Quale algoritmo è la tua risposta?
meder omuraliev,

@meder: il mio algoritmo è un algoritmo di unione. L'unione stessa viene eseguita in O (n + m), ma l'ordinamento richiede al massimo O (n · log n + m · log m). Quindi l'intero algoritmo è O (n · log n + m · log m).
Gumbo,

6

Si può fare 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>


6

Esistono così tante soluzioni per unire due array. Possono essere divisi in due categorie principali (tranne l'uso di librerie di terze parti come lodash o underscore.js).

a) combinare due matrici e rimuovere gli oggetti duplicati.

b) filtrare gli oggetti prima di combinarli.

Combina due matrici e rimuovi gli elementi duplicati

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

Unifying

Esistono molti modi per unificare un array, suggerisco personalmente di seguito due metodi.

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

Filtra gli oggetti prima di combinarli

Ci sono anche molti modi, ma personalmente suggerisco il codice qui sotto per la sua semplicità.

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));

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]

5
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

La cosa bella di questo è la prestazione e che in generale, quando si lavora con gli array, si stanno concatenando metodi come filtro, mappa, ecc. In modo da poter aggiungere quella linea e si concatenerà e deduplicherà array2 con array1 senza bisogno di un riferimento al successivo uno (quando stai concatenando metodi che non hai), esempio:

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

(Non mi piace inquinare Array.prototype e sarebbe l'unico modo di rispettare la catena - la definizione di una nuova funzione la spezzerà - quindi penso che qualcosa del genere sia l'unico modo per farlo)


4

Puoi provare questo:

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

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


3

Un approccio funzionale con ES2015

Seguire l'approccio funzionale a uniondi due Arrays è solo la composizione di concate filter. Al fine di fornire prestazioni ottimali ricorriamo al Settipo di dati nativo , che è ottimizzato per le ricerche di proprietà.

Comunque, la domanda chiave in combinazione con una unionfunzione è come trattare i duplicati. Sono possibili le seguenti permutazioni:

Array A      + Array B

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

Le prime due permutazioni sono facili da gestire con un'unica funzione. Tuttavia, gli ultimi due sono più complicati, dal momento che non è possibile elaborarli finché si fa affidamento sulle Setricerche. Poiché il passaggio a semplici Objectricerche di proprietà precedenti comporterebbe un grave calo delle prestazioni, l'implementazione seguente ignora solo la terza e la quarta permutazione. Dovresti creare una versione separata di unionsupportarli.


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

Da qui in poi diventa banale implementare una unionnfunzione, che accetta qualsiasi numero di array (ispirato ai commenti di 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) );

Si scopre che unionnè solo foldl(aka Array.prototype.reduce), che prende unioncome riduttore. Nota: poiché l'implementazione non utilizza un accumulatore aggiuntivo, genererà un errore quando lo si applica senza argomenti.


1
un paio di feedback: me ne sono accorto flipe notfsono inutilizzati. Anche unionByperdite predicati dettagli di implementazione (richiede conoscenza implicita di Settipo). Potrebbe essere carino se tu potessi fare qualcosa del genere: union = unionBy (apply)e unionci = unionBy (p => x => p(x.toLowerCase())). In questo modo l'utente invia semplicemente qualunque sia il valore di raggruppamento p- solo un'idea ^ _ ^
Grazie

zsmanca anche la dichiarazione variabile var/ letparola chiave
Grazie

1
ecco uno snippet di codice per chiarire [gist: unionBy.js ]
Grazie

@naomik Dopo aver ripensato la mia soluzione per un po 'non sono più così sicuro se è il modo giusto per passare il predicato. Tutto ciò che guadagni è una trasformazione di ogni elemento del secondo array. Mi chiedo se questo approccio risolva più di semplici problemi con i giocattoli.

3

per il gusto di farlo ... ecco una soluzione a linea singola:

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

Non particolarmente leggibile ma può aiutare qualcuno:

  1. Applica una funzione di riduzione con il valore iniziale dell'accumulatore impostato su un array vuoto.
  2. La funzione di riduzione utilizza concat per aggiungere ciascun sotto-array all'array di accumulatori.
  3. Il risultato è passato come parametro di costruzione per creare un nuovo Set.
  4. L'operatore spread viene utilizzato per convertire il Setin un array.
  5. La sort()funzione viene applicata al nuovo array.

2
Inoltre invece di reduce()te puoi usareArray.from(set)
Eran Goldin il

3

DeDuplica input singoli o Unisci e DeDuplica array multipli. Esempio sotto.

usando ES6 - Set, for of, destructuring

Ho scritto questa semplice funzione che accetta più argomenti di array. Praticamente è uguale alla soluzione sopra ha solo un caso d'uso più pratico. Questa funzione non concatena i valori duplicati in un solo array in modo da poterli eliminare in un secondo momento.

DEFINIZIONE BREVE FUNZIONE (solo 9 righe)

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

USARE ESEMPIO CODICE :

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

Perché usare arr.mapqui? Lo stai usando come foreach, poiché il risultato viene ignorato
Antony,

Ho usato return Array.from (set.values ​​()); , perché vscode fornisce un errore per 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]

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.