Codice più semplice per l'intersezione di array in javascript


611

Qual è il codice più semplice e privo di librerie per l'implementazione di intersezioni di array in JavaScript? Voglio scrivere

intersection([1,2,3], [2,3,4,5])

e prendi

[2, 3]

16
Vuoi semplice o veloce?
SLaks,

11
La priorità è semplice, ma non può essere così letale che sarà un maiale alle prestazioni :)
Peter,

4
Ho creato una pagina di test JsFiddle Banchmark per tutti i metodi qui, inclusa lafunzione di intersezione _underscore . (più alto è meglio) ! inserisci qui la descrizione dell'immagine Finora intersect_safe ha dato i migliori risultati . YOU & Underscore il peggio.
Neoswf,

L'aggiunta di a breaka Simple js loopsaumenta le operazioni / sec a ~ 10M
Richard

19
Nel caso in cui te ne accorgi : la risposta più semplice non è quella accettata, ma quello in basso: stackoverflow.com/questions/1885557/...
redben

Risposte:


1081

Utilizzare una combinazione di Array.prototype.filtere Array.prototype.indexOf:

array1.filter(value => -1 !== array2.indexOf(value))

O come ha suggerito vrugtehagel nei commenti, è possibile utilizzare il più recente Array.prototype.includesper un codice ancora più semplice:

array1.filter(value => array2.includes(value))

Per i browser più vecchi:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});

9
Che puoi risolvere aggiungendo una versione della libreria sul prototipo dell'array.
Anon.

12
Sì, ma valeva la pena menzionarlo.
Tim Down,

18
La migliore risposta qui, sia per semplicità che per lavorare con non numeri
Muhd

41
intersection([1,2,1,1,3], [1])ritorna [1, 1, 1]. Non dovrebbe tornare solo [1]?
Edjroot,

21
Invece di array2.indexOf(n) != -1uno si può anche scrivere array2.includes(n)per un codice ancora più semplice.
Vrugtehagel,

157

Il distruttivo sembra più semplice, specialmente se possiamo supporre che l'input sia ordinato:

/* destructively finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *  State of input arrays is undefined when
 *  the function returns.  They should be 
 *  (prolly) be dumped.
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length, b.length)
 */
function intersection_destructive(a, b)
{
  var result = [];
  while( a.length > 0 && b.length > 0 )
  {  
     if      (a[0] < b[0] ){ a.shift(); }
     else if (a[0] > b[0] ){ b.shift(); }
     else /* they're equal */
     {
       result.push(a.shift());
       b.shift();
     }
  }

  return result;
}

Non distruttivo deve essere un pelo più complicato, dal momento che dobbiamo tenere traccia degli indici:

/* finds the intersection of 
 * two arrays in a simple fashion.  
 *
 * PARAMS
 *  a - first array, must already be sorted
 *  b - second array, must already be sorted
 *
 * NOTES
 *
 *  Should have O(n) operations, where n is 
 *    n = MIN(a.length(), b.length())
 */
function intersect_safe(a, b)
{
  var ai=0, bi=0;
  var result = [];

  while( ai < a.length && bi < b.length )
  {
     if      (a[ai] < b[bi] ){ ai++; }
     else if (a[ai] > b[bi] ){ bi++; }
     else /* they're equal */
     {
       result.push(a[ai]);
       ai++;
       bi++;
     }
  }

  return result;
}

14
Ci sono numerosi errori in intersect_safe: lengthè una proprietà in array, non un metodo. C'è una variabile non sovrapposta iin result.push(a[i]);. Infine, questo semplicemente non funziona nel caso generale: due oggetti in cui nessuno dei due è maggiore dell'altro secondo l' >operatore non sono necessariamente uguali. intersect_safe( [ {} ], [ {} ] ), ad esempio, fornirà (una volta risolti gli errori menzionati in precedenza) un array con un elemento, che è chiaramente errato.
Tim Down,

1
@Tim Down: corretti gli errori di sintassi che hai sottolineato. Se sia corretto o errato considerare nulla, né gerater, né meno uguale, dipende dai requisiti. Non noto nulla nella domanda originale che afferma che il contenuto dovrebbe contenere array. Ora, avresti ragione a dire che l'input imprevisto dovrebbe essere gestito, ma se le specifiche già dettano quell'input devono essere matrici di numeri (come ho ipotizzato), allora il codice andrebbe bene.
atk,

1
@atk: prendo il tuo punto, visto che l'esempio nella domanda usa matrici che contengono solo numeri.
Tim Down,

4
È inoltre possibile utilizzare .slice(0)per creare un clone dell'array intersect_safe, anziché tenere traccia degli indici.
johnluetke,

1
@thesmart: hai ragione, ci sono sicuramente modi più performanti per farlo. Il codice sopra riportato doveva essere semplice, non veloce :)
atk

59

Se il tuo ambiente supporta ECMAScript 6 Set , un modo semplice e presumibilmente efficace (vedi collegamento alle specifiche):

function intersect(a, b) {
  var setA = new Set(a);
  var setB = new Set(b);
  var intersection = new Set([...setA].filter(x => setB.has(x)));
  return Array.from(intersection);
}

Più breve, ma meno leggibile (anche senza creare l'intersezione aggiuntiva Set):

function intersect(a, b) {
      return [...new Set(a)].filter(x => new Set(b).has(x));
}

Evitare una nuova Setda bogni volta:

function intersect(a, b) {
      var setB = new Set(b);
      return [...new Set(a)].filter(x => setB.has(x));
}

Nota che quando usi i set otterrai solo valori distinti, quindi new Set[1,2,3,3].sizevaluta 3.


1
cos'è questa [...setA]sintassi? Qualche tipo speciale di operazione javascript?
jxramos,

1
@jxramos che è la sintassi Spread e in questo caso viene appena utilizzato per creare un array dagli elementi nel set. "Array.from (setA)" funzionerebbe anche in questo caso, ma dal momento che la domanda posta per "più semplice" ho cercato di renderlo più pulito da leggere su quella riga. A tale proposito, penso che il codice potrebbe essere più semplice, quindi aggiornerò lo snippet.
nbarbosa,

@nbarbosa Sono curioso: perché hai "clonato" l'array a? #filter non distrugge l'array originale, giusto? Crea un nuovo array?
tra il

@bplittle Ho appena creato un array dal set per rimuovere i duplicati, altrimenti l'uso diretto dell'array comporterebbe la restituzione dei duplicati. Ad esempio, se avessi usato direttamente l'array, intersect ([1,2,2,4], [2,3]) avrebbe prodotto [2, 2].
nbarbosa,

2
Quella x => new Set(b).has(x)funzione freccia non si trasforma bin un set ogni volta che viene eseguita? Probabilmente dovresti salvare quel set in una variabile.
Aran-Fey,

39

Utilizzo di Underscore.js o lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]

20
Op ha chiesto "senza biblioteca".
LinuxDisciple

@LinuxDisciple Il mio errore per averlo perso. Grazie per le note
Sai Ram,

33
In ogni caso, questa è la prima lista di Google per questa ricerca, quindi è utile avere una risposta in biblioteca. Grazie.
webnoob,

Anch'io sono contento che sia stato pubblicato. La prima volta che ho sentito la necessità di underscore.js. Di solito la mappa JavaScript e la riduzione delle condutture svolgono il lavoro in modo elegante, ma non questa volta.
Sridhar Sarnobat,

I <3 Underscore e I <3 Jeremy Ashkenas (il suo creatore), ma anche così consiglio vivamente di dare un'occhiata a Lodash. È sostanzialmente una versione superiore di Underscore (originariamente era un fork) il cui unico aspetto negativo è il codice sorgente super-ottimizzato (e quindi quasi illeggibile). Le persone di Underscore hanno persino preso in considerazione l'idea di sbarazzarsi di Underscore (e semplicemente di dire alle persone di usare Lodash), ma le persone a cui interessa la leggibilità del codice sorgente hanno sostenuto di mantenerlo (in realtà ero da quella parte, ma da allora mi sono convertito a Lodash). @see github.com/jashkenas/underscore/issues/2182
machineghost

14

Il mio contributo in termini di ES6. In generale trova l'intersezione di una matrice con un numero indefinito di matrici fornite come argomenti.

Array.prototype.intersect = function(...a) {
  return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
}
var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]],
     arr = [0,1,2,3,4,5,6,7,8,9];

document.write("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");


questo codice ha un bell'aspetto, ma non lo capisco completamente. È possibile spiegarlo per favore?
theusual

1
@novembersky Riunisce tutti gli array di soggetti in un array come [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]e quindi si applica .reduce(). La prima [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)operazione viene eseguita e il risultato diventa il nuovo pe cdiventa [4,5,6,7]nel turno successivo e continua così fino a quando non rimane più nulla c.
Redu,

1
Questa è una soluzione costosa se si lavora con set di dati di grandi dimensioni.
Madbreaks,

1
Non modificare prototypeinutilmente.
fregante

14

// Return elements of array a that are also in b in linear time:
function intersect(a, b) {
  return a.filter(Set.prototype.has, new Set(b));
}

// Example:
console.log(intersect([1,2,3], [2,3,4,5]));

Raccomando una soluzione succinta che supera le altre implementazioni su input di grandi dimensioni. Se le prestazioni su piccoli ingressi sono importanti, controllare le alternative seguenti.

Alternative e confronto delle prestazioni:

Vedere il frammento seguente per implementazioni alternative e controllare https://jsperf.com/array-intersection-comparison per i confronti delle prestazioni.

Risultati in Firefox 53:

  • Ops / sec su array di grandi dimensioni (10.000 elementi):

    filter + has (this)               523 (this answer)
    for + has                         482
    for-loop + in                     279
    filter + in                       242
    for-loops                          24
    filter + includes                  14
    filter + indexOf                   10
  • Ops / sec su array di piccole dimensioni (100 elementi):

    for-loop + in                 384,426
    filter + in                   192,066
    for-loops                     159,137
    filter + includes             104,068
    filter + indexOf               71,598
    filter + has (this)            43,531 (this answer)
    filter + has (arrow function)  35,588

2
intersect([1,2,2,3], [2,3,4,5])ritorna [2, 2, 3].
SeregPie,

1
@SeregPie Esattamente. Come da commento "Restituisce elementi dell'array a che sono anche in b"
le_m,

Risposta di qualità, tuttavia l'uso di Sets altera sostanzialmente i risultati poiché la domanda di op si è posta solo sulle intersezioni di array e non fa menzione / stipulazione di come gestire i duplicati. Timido di ciò, questa risposta può produrre risultati inaspettati quando esistono duplicati.
Madbreaks,

1
Lo adoro, ma hai aggiunto una funzione inutile con "filtro + include". provare a.filter(b.includes). Dovrebbe funzionare in modo considerevolmente più veloce (uguale all'aggiornamento della funzione).
SEoF

11

Che ne dici di usare solo array associativi?

function intersect(a, b) {
    var d1 = {};
    var d2 = {};
    var results = [];
    for (var i = 0; i < a.length; i++) {
        d1[a[i]] = true;
    }
    for (var j = 0; j < b.length; j++) {
        d2[b[j]] = true;
    }
    for (var k in d1) {
        if (d2[k]) 
            results.push(k);
    }
    return results;
}

modificare:

// new version
function intersect(a, b) {
    var d = {};
    var results = [];
    for (var i = 0; i < b.length; i++) {
        d[b[i]] = true;
    }
    for (var j = 0; j < a.length; j++) {
        if (d[a[j]]) 
            results.push(a[j]);
    }
    return results;
}

1
Ciò è possibile solo se le tue matrici contengono solo stringhe o numeri e se nessuno degli script nella tua pagina ha incasinato Object.prototype.
Tim Down,

2
L'esempio dell'OP stava usando i numeri e se uno script ha incasinato Object.prototype, lo script dovrebbe essere riscritto o rimosso.
Steven Huwig,

Non hai bisogno di entrambi (d1) e (d2). Crea (d2), quindi esegui il ciclo attraverso (a) anziché il ciclo (d1).
StanleyH,

Dovrebbe essere d[b[i]] = true;invece di d[b[j]] = true;( inon j). Ma la modifica richiede 6 caratteri.
Izhaki,

@Izhaki grazie, risolto. (Aggiunto un // commento per aggirare il requisito minimo di modifica.)
Steven Huwig

8
  1. Ordina
  2. controlla uno per uno dall'indice 0, crea un nuovo array da quello.

Qualcosa del genere, non testato bene però.

function intersection(x,y){
 x.sort();y.sort();
 var i=j=0;ret=[];
 while(i<x.length && j<y.length){
  if(x[i]<y[j])i++;
  else if(y[j]<x[i])j++;
  else {
   ret.push(x[i]);
   i++,j++;
  }
 }
 return ret;
}

alert(intersection([1,2,3], [2,3,4,5]));

PS: l'algoritmo previsto solo per Numbers e String normali, l'intersezione di matrici di oggetti arbitrari potrebbe non funzionare.


3
L'ordinamento non sarà necessariamente di aiuto per le matrici di oggetti arbitrari
Tim Down

Se la matrice non è ordinata, necessità di anello intorno 1.000.000 volte quando si intersecare 1000 lunghezza dell'array x 1000 lunghezza dell'array
TU

Penso che tu abbia perso il mio punto, che è che gli oggetti arbitrari in JavaScript non hanno un ordinamento naturale, il che significa che l'ordinamento di una matrice di oggetti arbitrari non comporterà il raggruppamento di oggetti uguali. Non va bene avere un algoritmo efficiente che non funziona.
Tim Down

Ah scusa, mi sono perso "oggetti arbitrari", sì, hai ragione. questi oggetti non possono ordinarli e l'algoritmo potrebbe non funzionare su quelli.
TU il

8

Le prestazioni dell'implementazione di @ atk per le matrici ordinate di primitive possono essere migliorate usando .pop anziché .shift.

function intersect(array1, array2) {
   var result = [];
   // Don't destroy the original arrays
   var a = array1.slice(0);
   var b = array2.slice(0);
   var aLast = a.length - 1;
   var bLast = b.length - 1;
   while (aLast >= 0 && bLast >= 0) {
      if (a[aLast] > b[bLast] ) {
         a.pop();
         aLast--;
      } else if (a[aLast] < b[bLast] ){
         b.pop();
         bLast--;
      } else /* they're equal */ {
         result.push(a.pop());
         b.pop();
         aLast--;
         bLast--;
      }
   }
   return result;
}

Ho creato un benchmark usando jsPerf: http://bit.ly/P9FrZK . È circa tre volte più veloce usare .pop.


1
Proprio come una nota a margine per gli altri - questo funzionerà solo per i numeri, non per le stringhe.
Izhaki,

Si noti che se si sostituisce a[aLast] > b[bLast]con a[aLast].localeCompare(b[bLast]) > 0(e lo stesso con il else ifseguente), questo funzionerà sulle stringhe.
Andrew

1
La differenza di velocità dipende dalla dimensione delle matrici perché .popè O (1) ed .shift()è O (n)
Esailija

8

Utilizzando jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);

8
Questo potrebbe anche essere scritto come c = $(b).filter(a);, ma non consiglierei di fare affidamento su jQuery per questo tipo di manipolazione dell'array poiché la documentazione menziona solo che funziona per gli elementi.
Stryner,

1
Questo non risponde alla domanda dell'op: "Qual è il codice più semplice e privo di librerie ..."
Madbreaks,

7

Per le matrici che contengono solo stringhe o numeri puoi fare qualcosa con l'ordinamento, come per alcune delle altre risposte. Nel caso generale di matrici di oggetti arbitrari, non penso che tu possa evitare di farlo a lungo. Quanto segue fornirà l'intersezione di qualsiasi numero di matrici fornite come parametri per arrayIntersection:

var arrayContains = Array.prototype.indexOf ?
    function(arr, val) {
        return arr.indexOf(val) > -1;
    } :
    function(arr, val) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === val) {
                return true;
            }
        }
        return false;
    };

function arrayIntersection() {
    var val, arrayCount, firstArray, i, j, intersection = [], missing;
    var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array

    // Search for common values
    firstArray = arrays.pop();
    if (firstArray) {
        j = firstArray.length;
        arrayCount = arrays.length;
        while (j--) {
            val = firstArray[j];
            missing = false;

            // Check val is present in each remaining array 
            i = arrayCount;
            while (!missing && i--) {
                if ( !arrayContains(arrays[i], val) ) {
                    missing = true;
                }
            }
            if (!missing) {
                intersection.push(val);
            }
        }
    }
    return intersection;
}

arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"]; 

Questo funziona solo nel caso in cui l'identità dell'oggetto sia l'unica forma di uguaglianza.
Steven Huwig,

Bene sì, ma penso che sia quello che sembrerebbe naturale alla maggior parte delle persone. È anche banale collegare una funzione alternativa per eseguire un diverso test di uguaglianza.
Tim Down,

Penso che tu stia creando accidentalmente prima una variabile globaleArr nel tuo esempio.
Jason Jackson,

@JasonJackson: hai ragione, grazie. Chiaramente mi è venuto in mente se chiamare la variabile firstArro meno firstArraye non ho aggiornato tutti i riferimenti. Fisso.
Tim Down

7

È piuttosto breve con ES2015 e set. Accetta valori simili a array come una stringa e rimuove i duplicati.

let intersection = function(a, b) {
  a = new Set(a), b = new Set(b);
  return [...a].filter(v => b.has(v));
};

console.log(intersection([1,2,1,2,3], [2,3,5,4,5,3]));

console.log(intersection('ccaabbab', 'addb').join(''));


Converti da Set a array con [... a] rimuoverà gli elementi duplicati, buona idea, grazie
V-SHY

1
Questa soluzione è stata fornita due volte prima della tua.
Madbreaks,

7

È possibile utilizzare un Setas thisArgof Array#filtere prendere Set#hascome callback.

function intersection(a, b) {
    return a.filter(Set.prototype.has, new Set(b));
}

console.log(intersection([1, 2, 3], [2, 3, 4, 5]));


Non so perché questo non abbia ottenuto voti più alti. È chiaramente la risposta migliore.
Paul Rooney,

5

Una piccola modifica alla più piccola qui (la soluzione filter / indexOf ), ovvero la creazione di un indice dei valori in una delle matrici utilizzando un oggetto JavaScript, lo ridurrà da O (N * M) a tempo "probabilmente" lineare. source1 source2

function intersect(a, b) {
  var aa = {};
  a.forEach(function(v) { aa[v]=1; });
  return b.filter(function(v) { return v in aa; });
}

Questa non è la soluzione più semplice (è più codice del filtro + indexOf ), né è la più veloce (probabilmente più lenta di un fattore costante di intersect_safe () ), ma sembra un equilibrio piuttosto buono. È molto semplice, pur offrendo buone prestazioni e non richiede input pre-ordinati.


5

Un altro approccio indicizzato in grado di elaborare un numero qualsiasi di matrici contemporaneamente:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = 0;
            index[v]++;
        };
    };
    var retv = [];
    for (var i in index) {
        if (index[i] == arrLength) retv.push(i);
    };
    return retv;
};

Funziona solo per valori che possono essere valutati come stringhe e dovresti passarli come array come:

intersect ([arr1, arr2, arr3...]);

... ma accetta in modo trasparente oggetti come parametro o come uno qualsiasi degli elementi da intersecare (restituendo sempre array di valori comuni). Esempi:

intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]
intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]

EDIT: Ho appena notato che questo è, in un certo senso, leggermente difettoso.

Cioè: l'ho codificato pensando che le matrici di input non possano contenere di per sé ripetizioni (come nell'esempio fornito non).

Ma se le matrici di input contengono ripetizioni, ciò produrrebbe risultati errati. Esempio (utilizzando l'implementazione di seguito):

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);
// Expected: [ '1' ]
// Actual: [ '1', '3' ]

Fortunatamente questo è facile da risolvere semplicemente aggiungendo l'indicizzazione di secondo livello. Questo è:

Modificare:

        if (index[v] === undefined) index[v] = 0;
        index[v]++;

di:

        if (index[v] === undefined) index[v] = {};
        index[v][i] = true; // Mark as present in i input.

...e:

         if (index[i] == arrLength) retv.push(i);

di:

         if (Object.keys(index[i]).length == arrLength) retv.push(i);

Esempio completo:

// Calculate intersection of multiple array or object values.
function intersect (arrList) {
    var arrLength = Object.keys(arrList).length;
        // (Also accepts regular objects as input)
    var index = {};
    for (var i in arrList) {
        for (var j in arrList[i]) {
            var v = arrList[i][j];
            if (index[v] === undefined) index[v] = {};
            index[v][i] = true; // Mark as present in i input.
        };
    };
    var retv = [];
    for (var i in index) {
        if (Object.keys(index[i]).length == arrLength) retv.push(i);
    };
    return retv;
};

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ]

2
Questa è di gran lunga la risposta migliore con una piccola modifica. Dopo la var v = riga aggiungi if (typeof v == 'function') continue;e salterà l'aggiunta di funzioni ai risultati. Grazie!
Zsolti,

Grazie @Zsolti. Non aggiungo il tuo suggerimento perché avere funzioni (e il modo in cui vogliamo gestirlo) non rientra nell'ambito della domanda originale. Ma vedi la mia modifica: se puoi avere ripetizioni negli array di input, l'implementazione originale è errata. L'ho corretto nella mia modifica. ... D'altra parte, se sai per certo che non ci sarà alcuna ripetizione, l'implementazione originale è leggermente più economica.
Bitifet,

... riguardo alle funzioni, possono anche essere intersecate: se le rileviamo come dice @Zsolti (con if (typeof v == 'function'), allora possiamo usare la sua stringa ( v.toString()) come chiave per l'indice. Ma dobbiamo fare qualcosa per preservarlo intatto. Il modo più semplice per farlo è semplicemente assegnare la funzione originale come valore anziché un semplice valore vero booleano, ma, in tal caso, anche l'ultimo deindexaton dovrebbe essere modificato per rilevare questa condizione e ripristinare il giusto valore (la funzione).
bitifet

Quanto può essere veloce con 30 array con 100 elementi .. come è l'utilizzo della CPU?
aidonsnous,

5

Puoi usare (per tutti i browser tranne IE):

const intersection = array1.filter(element => array2.includes(element));

o per IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);

Sarebbe bello se potessi trasformarlo in una funzione
valanga1

@ valanga1 const intersection = (a1, a2) => a1.filter (e => a2.includes (e));
jota3,

4
function intersection(A,B){
var result = new Array();
for (i=0; i<A.length; i++) {
    for (j=0; j<B.length; j++) {
        if (A[i] == B[j] && $.inArray(A[i],result) == -1) {
            result.push(A[i]);
        }
    }
}
return result;
}

4

Con alcune restrizioni sui tuoi dati, puoi farlo in modo lineare tempo !

Per numeri interi positivi : utilizzare un array che associa i valori a un valore booleano "visto / non visto".

function intersectIntegers(array1,array2) { 
   var seen=[],
       result=[];
   for (var i = 0; i < array1.length; i++) {
     seen[array1[i]] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if ( seen[array2[i]])
        result.push(array2[i]);
   }
   return result;
}

Esiste una tecnica simile per gli oggetti : prendi una chiave fittizia, impostala su "true" per ogni elemento in array1, quindi cerca questa chiave in elementi di array2. Pulisci quando hai finito.

function intersectObjects(array1,array2) { 
   var result=[];
   var key="tmpKey_intersect"
   for (var i = 0; i < array1.length; i++) {
     array1[i][key] = true;
   }
   for (var i = 0; i < array2.length; i++) {
     if (array2[i][key])
        result.push(array2[i]);
   }
   for (var i = 0; i < array1.length; i++) {
     delete array1[i][key];
   }
   return result;
}

Ovviamente devi assicurarti che la chiave non sia stata visualizzata prima, altrimenti distruggerai i tuoi dati ...


A proposito, questo può essere facilmente esteso per intercettare un numero qualsiasi di matrici: sostituire il valore booleano con numeri interi e incrementare ogni volta che viene visualizzato: è possibile leggere facilmente l'intersezione nell'ultimo round.
Tarulen,

Soluzione interessante, mi piace. La maggior parte delle altre soluzioni sono O (n ^ 2), ma questa è O (n). Ho aggiunto il codice intero al violino delle prestazioni di ericP qui jsfiddle.net/321juyLu/2 . È arrivato terzo, mi piace :)
rmcsharry,

3

Contribuirò con ciò che ha funzionato meglio per me:

if (!Array.prototype.intersect){
Array.prototype.intersect = function (arr1) {

    var r = [], o = {}, l = this.length, i, v;
    for (i = 0; i < l; i++) {
        o[this[i]] = true;
    }
    l = arr1.length;
    for (i = 0; i < l; i++) {
        v = arr1[i];
        if (v in o) {
            r.push(v);
        }
    }
    return r;
};
}

3

"indexOf" per IE 9.0, chrome, firefox, opera,

    function intersection(a,b){
     var rs = [], x = a.length;
     while (x--) b.indexOf(a[x])!=-1 && rs.push(a[x]);
     return rs.sort();
    }

intersection([1,2,3], [2,3,4,5]);
//Result:  [2,3]

2

'use strict'

// Example 1
function intersection(a1, a2) {
    return a1.filter(x => a2.indexOf(x) > -1)
}

// Example 2 (prototype function)
Array.prototype.intersection = function(arr) {
    return this.filter(x => arr.indexOf(x) > -1)
} 

const a1 = [1, 2, 3]
const a2 = [2, 3, 4, 5]

console.log(intersection(a1, a2))
console.log(a1.intersection(a2))


2

Un approccio funzionale con ES2015

Un approccio funzionale deve considerare l'utilizzo di funzioni pure senza effetti collaterali, ognuno dei quali riguarda solo un singolo lavoro.

Queste restrizioni migliorano la componibilità e la riusabilità delle funzioni coinvolte.

// small, reusable auxiliary functions

const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const apply = f => x => f(x);


// intersection

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


// mock data

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


// run it

console.log( intersect(xs) (ys) );

Si noti che Setviene utilizzato il tipo nativo , che presenta prestazioni di ricerca vantaggiose.

Evita i duplicati

Ovviamente gli oggetti che si verificano ripetutamente dal primo Arrayvengono conservati, mentre il secondo Arrayviene de-duplicato. Questo può essere o non essere il comportamento desiderato. Se hai bisogno di un risultato univoco basta applicare dedupeal primo argomento:

// auxiliary functions

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


// intersection

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


// de-duplication

const dedupe = comp(afrom) (createSet);


// mock data

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


// unique result

console.log( intersect(dedupe(xs)) (ys) );

Calcola l'intersezione di un numero qualsiasi di Arrays

Se si vuole calcolare l'intersezione di un numero arbitrario di Arrays appena comporre intersectcon foldl. Ecco una funzione di convenienza:

// auxiliary functions

const apply = f => x => f(x);
const uncurry = f => (x, y) => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);


// intersection

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


// intersection of an arbitrarily number of Arrays

const intersectn = (head, ...tail) => foldl(intersect) (head) (tail);


// mock data

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


// run

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


Incredibilmente funzionale: ho dovuto fare una doppia ripresa per confermare che non è Haskell. L'unico nitpick è: (expr ? true : false)è ridondante. Usa solo exprse non sono necessari veri booleani, solo verità / falsità.
jose_castro_arnaud,

2

Per semplicità:

// Usage
const intersection = allLists
  .reduce(intersect, allValues)
  .reduce(removeDuplicates, []);


// Implementation
const intersect = (intersection, list) =>
  intersection.filter(item =>
    list.some(x => x === item));

const removeDuplicates = (uniques, item) =>
  uniques.includes(item) ? uniques : uniques.concat(item);


// Example Data
const somePeople = [bob, doug, jill];
const otherPeople = [sarah, bob, jill];
const morePeople = [jack, jill];

const allPeople = [...somePeople, ...otherPeople, ...morePeople];
const allGroups = [somePeople, otherPeople, morePeople];

// Example Usage
const intersection = allGroups
  .reduce(intersect, allPeople)
  .reduce(removeDuplicates, []);

intersection; // [jill]

Benefici:

  • sporco semplice
  • data-centric
  • funziona per un numero arbitrario di liste
  • funziona per lunghezze arbitrarie di liste
  • funziona per tipi di valori arbitrari
  • funziona per un ordinamento arbitrario
  • mantiene la forma (ordine di prima apparizione in qualsiasi array)
  • esce presto dove possibile
  • memoria sicura, senza manomissioni con i prototipi Function / Array

svantaggi:

  • maggiore utilizzo della memoria
  • maggiore utilizzo della CPU
  • richiede una comprensione di ridurre
  • richiede comprensione del flusso di dati

Non vorresti usarlo per il motore 3D o il lavoro del kernel, ma se hai problemi a farlo funzionare in un'app basata su eventi, il tuo design ha problemi più grandi.


2

.reduceper costruire una mappa e .filtertrovare l'intersezione. deleteall'interno di .filterci permette di trattare il secondo array come se fosse un set unico.

function intersection (a, b) {
  var seen = a.reduce(function (h, k) {
    h[k] = true;
    return h;
  }, {});

  return b.filter(function (k) {
    var exists = seen[k];
    delete seen[k];
    return exists;
  });
}

Trovo questo approccio abbastanza facile da ragionare. Si esibisce in tempo costante.


2

Questo è probabilmente il più semplice, oltre a list1.filter (n => list2.includes (n))

var list1 = ['bread', 'ice cream', 'cereals', 'strawberry', 'chocolate']
var list2 = ['bread', 'cherry', 'ice cream', 'oats']

function check_common(list1, list2){
	
	list3 = []
	for (let i=0; i<list1.length; i++){
		
		for (let j=0; j<list2.length; j++){	
			if (list1[i] === list2[j]){
				list3.push(list1[i]);				
			}		
		}
		
	}
	return list3
	
}

check_common(list1, list2) // ["bread", "ice cream"]


questo ha O (nm) complessità temporale ... questo potrebbe essere risolto in O (n + m)
alchuang

2

Se è necessario che lo gestisca intersecando più array:

const intersect = (a, b, ...rest) => {
  if (rest.length === 0) return [...new Set(a)].filter(x => new Set(b).has(x));
  return intersect(a, intersect(b, ...rest));
};

console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) // [1,2]


Ma quanto è veloce questa soluzione per 30 array con 100 elementi?
aidonsnous,

Questo non utilizza altro che metodi nativi di Javascript e pertanto la VM in cui verrà eseguito il codice è gratuita per ottimizzarlo il più possibile. Sono abbastanza sicuro che non esiste una soluzione più veloce dato che lo stai eseguendo in una versione moderna di V8 rispetto all'età di questo commento.
Belfordz,

2

Modo semplice stile ES6.

const intersection = (a, b) => {
  const s = new Set(b);
  return a.filter(x => s.has(x));
};

Esempio:

intersection([1, 2, 3], [4, 3, 2]); // [2, 3]

2

Ho scritto una funzione di intesection che può persino rilevare l'intersezione di array di oggetti in base alla particolare proprietà di quegli oggetti.

Per esempio,

if arr1 = [{id: 10}, {id: 20}]
and arr2 =  [{id: 20}, {id: 25}]

e vogliamo intersezione in base alla idproprietà, quindi l'output dovrebbe essere:

[{id: 20}]

Pertanto, la funzione per lo stesso (nota: codice ES6) è:

const intersect = (arr1, arr2, accessors = [v => v, v => v]) => {
    const [fn1, fn2] = accessors;
    const set = new Set(arr2.map(v => fn2(v)));
    return arr1.filter(value => set.has(fn1(value)));
};

e puoi chiamare la funzione come:

intersect(arr1, arr2, [elem => elem.id, elem => elem.id])

Nota anche: questa funzione trova l'intersezione considerando che il primo array è l'array primario e quindi il risultato dell'intersezione sarà quello dell'array primario.


2

Pensa che questo sarà più veloce con O (array1 + array2) assumendo che map.has () sia ~ O (1). Correggimi se sbagli.

const intersection = (a1, a2) => {
  let map = new Map();
  let result = []
  for (let i of a1) {
    if (!map.has(i)) map.set(i, true);
  }
  for (let i of a2) {
    if (map.has(i)) result.push(i)
  }
  return result;
}


1

Ecco l' implementazione underscore.js :

_.intersection = function(array) {
  if (array == null) return [];
  var result = [];
  var argsLength = arguments.length;
  for (var i = 0, length = array.length; i < length; i++) {
    var item = array[i];
    if (_.contains(result, item)) continue;
    for (var j = 1; j < argsLength; j++) {
      if (!_.contains(arguments[j], item)) break;
    }
    if (j === argsLength) result.push(item);
  }
  return result;
};

Fonte: http://underscorejs.org/docs/underscore.html#section-62


Non è un cattivo riferimento se undesrcore è disponibile
Dimitrios Mistriotis,
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.