Einfachster Code für Array-Schnittpunkte in Javascript

607

Was ist der einfachste, bibliotheksfreie Code zum Implementieren von Array-Schnittpunkten in Javascript? Ich möchte schreiben

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

und bekomme

[2, 3]
Peter
quelle
16
Willst du einfach oder schnell?
SLaks
11
Priorität ist einfach, aber es kann nicht so hirntot sein, dass es ein Performance-Schwein wird :)
Peter
4
Ich habe eine JsFiddle Banchmark-Testseite für alle Methoden hier erstellt, einschließlich der Schnittfunktion _underscore . (höher ist besser) ! Geben Sie hier die Bildbeschreibung ein. Bis jetzt hat intersect_safe die besten Ergebnisse erzielt . SIE & unterstreichen das Schlimmste.
Neoswf
Hinzufügen eines breakzu Simple js loopserhöht die ops / s ~ 10M
Richard
19
Falls Sie es verpassen : Die einfachste Antwort ist nicht die akzeptierte, sondern die unten stehende: stackoverflow.com/questions/1885557/…
redben

Antworten:

1078

Verwenden Sie eine Kombination aus Array.prototype.filterund Array.prototype.indexOf:

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

Oder wie vrugtehagel in den Kommentaren vorgeschlagen hat, können Sie den neueren Array.prototype.includesfür noch einfacheren Code verwenden:

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

Für ältere Browser:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});
Anon.
quelle
9
Was Sie lösen können, indem Sie dem Prototyp des Arrays eine Bibliotheksversion hinzufügen.
Anon.
12
Ja, aber es war erwähnenswert.
Tim Down
18
Beste Antwort hier, sowohl der Einfachheit halber als auch der Arbeit mit Nicht-Zahlen
Muhd
41
intersection([1,2,1,1,3], [1])kehrt zurück [1, 1, 1]. Sollte es nicht einfach zurückkehren [1]?
Edjroot
21
Stattdessen array2.indexOf(n) != -1kann man auch array2.includes(n)für noch einfacheren Code schreiben .
vrugtehagel
157

Destruktiv scheint am einfachsten zu sein, besonders wenn wir davon ausgehen können, dass die Eingabe sortiert ist:

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

Zerstörungsfrei muss ein Haar komplizierter sein, da wir Indizes verfolgen müssen:

/* 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;
}
atk
quelle
14
Es gibt zahlreiche Fehler in intersect_safe: lengthist eine Eigenschaft in Arrays, keine Methode. Es gibt eine nicht freigegebene Variable iin result.push(a[i]);. Schließlich funktioniert dies im allgemeinen Fall einfach nicht: Zwei Objekte, bei denen laut >Operator keines größer als das andere ist, sind nicht unbedingt gleich. intersect_safe( [ {} ], [ {} ] )Beispielsweise wird (sobald die zuvor genannten Fehler behoben sind) ein Array mit einem Element angegeben, was eindeutig falsch ist.
Tim Down
1
@ Tim Down: Die Syntaxfehler, auf die Sie hingewiesen haben, wurden korrigiert. Ob es richtig oder falsch ist, etwas, das weder gerater noch weniger ist, als gleich zu betrachten, hängt von den Anforderungen ab. In der ursprünglichen Frage fällt mir nichts auf, was besagt, dass der Inhalt Arrays enthalten soll. Nun, Sie würden zu Recht sagen, dass unerwartete Eingaben behandelt werden sollten, aber wenn die Spezifikation bereits vorschrieb, dass Eingaben Arrays von Zahlen sein müssen (wie ich angenommen habe), wäre der Code in Ordnung.
Atk
1
@atk: Ich verstehe Ihren Standpunkt, da das Beispiel in der Frage Arrays verwendet, die nur Zahlen enthalten.
Tim Down
4
Sie können auch .slice(0)einen Klon des Arrays erstellen intersect_safe, anstatt Indizes zu verfolgen.
Johnluetke
1
@thesmart: du hast recht, es gibt definitiv performantere möglichkeiten. Der obige Code sollte einfach und nicht schnell sein :)
atk
58

Wenn Ihre Umgebung ECMAScript 6 Set unterstützt , gibt es eine einfache und vermeintlich effiziente Möglichkeit (siehe Spezifikationslink):

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

Kürzer, aber weniger lesbar (auch ohne die zusätzliche Kreuzung zu erstellen Set):

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

Vermeiden Sie eine neue Setaus bjeder Zeit:

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

Beachten Sie, dass Sie bei Verwendung von Sets nur unterschiedliche Werte erhalten, die daher zu new Set[1,2,3,3].sizeausgewertet werden 3.

nbarbosa
quelle
1
Was ist diese [...setA]Syntax? Eine spezielle Art von Javascript-Operation?
Jxramos
1
@jxramos ist die Spread-Syntax. In diesem Fall wird sie nur zum Erstellen eines Arrays aus den Elementen in der Menge verwendet. "Array.from (setA)" würde auch in diesem Fall funktionieren, aber da die Frage nach "am einfachsten" gestellt wurde, habe ich versucht, das Lesen in dieser Zeile sauberer zu gestalten. In dieser Hinsicht denke ich, dass der Code einfacher sein könnte, also werde ich das Snippet aktualisieren.
Nbarbosa
@nbarbosa Ich bin neugierig: Warum hast du das Array a "geklont"? #filter zerstört das ursprüngliche Array nicht, oder? Es erstellt ein neues Array?
Bplittle
@bplittle Ich habe gerade ein Array aus dem Set erstellt, um Duplikate zu entfernen. Andernfalls würde die direkte Verwendung des Arrays zur Rückgabe von Duplikaten führen. Wenn ich zum Beispiel das Array direkt verwenden würde, würde intersect ([1,2,2,4], [2,3]) [2, 2] ergeben.
Nbarbosa
2
Nicht , dass x => new Set(b).has(x)wiederum Pfeil Funktion bin eine Reihe jedes Mal ausgeführt wird ? Sie sollten diesen Satz wahrscheinlich in einer Variablen speichern.
Aran-Fey
39

Verwenden von Underscore.js oder lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]
Sai Ram
quelle
20
Op fragte nach "bibliotheksfrei".
LinuxDisciple
@LinuxDisciple Mein Fehler, dass ich das verpasst habe. Danke für Notizen
Sai Ram
33
In jedem Fall ist dies die Top-Google-Liste für diese Suche, daher ist eine Bibliotheksantwort hilfreich. Vielen Dank.
Webnoob
Ich bin auch froh, dass dies gepostet wurde. Zum ersten Mal hatte ich das Bedürfnis nach underscore.js. Normalerweise erledigen JavaScript-Zuordnungen und Reduzieren von Pipelines die Aufgabe elegant, diesmal jedoch nicht.
Sridhar Sarnobat
Ich <3 Underscore und ich <3 Jeremy Ashkenas (sein Schöpfer), aber trotzdem empfehle ich dringend, stattdessen Lodash auszuprobieren. Es ist im Grunde eine überlegene Version von Underscore (es war ursprünglich eine Gabelung), deren einziger Nachteil der superoptimierte (und damit fast unlesbare) Quellcode ist. Die Underscore-Leute haben sogar in Betracht gezogen, Underscore vollständig loszuwerden (und nur den Leuten zu sagen, dass sie Lodash verwenden sollen), aber Leute, die sich für die Lesbarkeit des Quellcodes interessieren, haben sich dafür ausgesprochen, ihn beizubehalten (ich war eigentlich auf dieser Seite, aber seitdem bin ich zu Lodash konvertiert). @see github.com/jashkenas/underscore/issues/2182
machineghost
14

Mein Beitrag in ES6-Begriffen. Im Allgemeinen wird der Schnittpunkt eines Arrays mit einer unbestimmten Anzahl von Arrays gefunden, die als Argumente bereitgestellt werden.

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

Reduzieren
quelle
Dieser Code sieht gut aus, aber ich verstehe ihn nicht vollständig. Kann man es bitte erklären?
Theusual
1
@novembersky Es sammelt alle Betreff-Arrays in einem Array wie [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]]und gilt dann .reduce(). Die erste [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)Operation wird ausgeführt und das Ergebnis wird das neue pund cwird [4,5,6,7]in der nächsten Runde und setzt sich fort, bis keine mehr cübrig ist.
Reduzieren Sie den
1
Dies ist eine teure Lösung, wenn Sie mit großen Datenmengen arbeiten.
Madbreaks
1
Ändern Sie das nicht prototypeunnötig.
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]));

Ich empfehle die oben genannte prägnante Lösung, die andere Implementierungen bei großen Eingaben übertrifft. Wenn die Leistung bei kleinen Eingaben wichtig ist, überprüfen Sie die folgenden Alternativen.

Alternativen und Leistungsvergleich:

Weitere Implementierungen finden Sie im folgenden Snippet. Unter https://jsperf.com/array-intersection-comparison finden Sie Leistungsvergleiche.

Ergebnisse in Firefox 53:

  • Ops / Sek. Auf großen Arrays (10.000 Elemente):

    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 / Sek. Auf kleinen Arrays (100 Elemente):

    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
le_m
quelle
2
intersect([1,2,2,3], [2,3,4,5])kehrt zurück [2, 2, 3].
SeregPie
1
@ SeregPie Genau.
Gemäß
Eine qualitativ hochwertige Antwort, jedoch ändert die Verwendung von Sets die Ergebnisse grundlegend, da in der Frage von op nur nach Array-Schnittpunkten gefragt wird und nicht erwähnt / festgelegt wird, wie mit Duplikaten umgegangen werden soll. Schüchtern kann diese Antwort zu unerwarteten Ergebnissen führen, wenn Duplikate vorhanden sind.
Madbreaks
1
Ich liebe es, aber Sie haben eine unnötige Funktion mit "Filter + Includes" hinzugefügt. versuche es a.filter(b.includes). Es sollte erheblich schneller laufen (genau wie Ihr Funktions-Upgrade).
SEoF
11

Wie wäre es nur mit assoziativen Arrays?

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

bearbeiten:

// 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;
}
Steven Huwig
quelle
1
Dies ist nur dann möglich, wenn Ihre Arrays nur Zeichenfolgen oder Zahlen enthalten und wenn keines der Skripte auf Ihrer Seite Fehler aufweist Object.prototype.
Tim Down
2
Im Beispiel des OP wurden Zahlen verwendet. Wenn ein Skript mit Object.prototype in Konflikt geraten ist, sollte das Skript neu geschrieben oder entfernt werden.
Steven Huwig
Sie brauchen nicht beide (d1) und (d2). Erstellen Sie (d2) und durchlaufen Sie dann (a), anstatt (d1) zu durchlaufen.
StanleyH
Sollte d[b[i]] = true;statt d[b[j]] = true;( inicht j) sein. Für die Bearbeitung sind jedoch 6 Zeichen erforderlich.
Izhaki
@ Izhaki danke, behoben. (Ein // Kommentar wurde hinzugefügt, um die Mindestanforderungen für die Bearbeitung
Steven Huwig
8
  1. Ordne es
  2. Überprüfen Sie nacheinander den Index 0 und erstellen Sie daraus ein neues Array.

So etwas, aber nicht gut getestet.

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: Der Algorithmus ist nur für Zahlen und normale Zeichenfolgen vorgesehen. Der Schnittpunkt von Arbitary Object Arrays funktioniert möglicherweise nicht.

DU
quelle
3
Das Sortieren hilft nicht unbedingt bei Arrays beliebiger Objekte
Tim Down
Wenn das Array nicht sortiert ist, müssen Sie etwa 1.000.000 Mal eine Schleife
SIE
Ich denke, Sie haben meinen Punkt verfehlt, nämlich dass beliebige Objekte in JavaScript keine natürliche Sortierreihenfolge haben, was bedeutet, dass das Sortieren eines Arrays beliebiger Objekte nicht dazu führt, dass gleiche Objekte gruppiert werden. Es ist nicht gut, einen effizienten Algorithmus zu haben, der nicht funktioniert.
Tim Down
Tut mir leid, ich habe "beliebige Objekte" verpasst, ja, du hast recht. Diese Objekte können es nicht sortieren, und der Algorithmus funktioniert möglicherweise nicht mit diesen.
SIE
8

Die Leistung der Implementierung von @ atk für sortierte Arrays von Grundelementen kann durch Verwendung von .pop anstelle von .shift verbessert werden.

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

Ich habe mit jsPerf einen Benchmark erstellt: http://bit.ly/P9FrZK . Es ist ungefähr dreimal schneller, .pop zu verwenden.

xn.
quelle
1
Nur als Randnotiz für andere - dies funktioniert nur für Zahlen, nicht für Zeichenfolgen.
Izhaki
Beachten Sie, dass , wenn Sie ersetzen a[aLast] > b[bLast]mit a[aLast].localeCompare(b[bLast]) > 0(und gleichen mit der weiter else ifunten) , dann wird dies auf Strings arbeiten.
Andrew
1
Der Geschwindigkeitsunterschied hängt von der Größe der Arrays ab, da er .popO (1) und .shift()O (n) ist
Esailija
8

Verwenden von jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);
Gowsikan
quelle
8
Dies könnte auch so geschrieben werden c = $(b).filter(a);, aber ich würde nicht empfehlen, sich für diese Art der Array-Manipulation auf jQuery zu verlassen, da in der Dokumentation nur erwähnt wird, dass es für Elemente funktioniert.
Stryner
1
Dies beantwortet nicht die Frage von op: "Was ist der einfachste, bibliotheksfreie Code ..."
Madbreaks
7

Bei Arrays, die nur Zeichenfolgen oder Zahlen enthalten, können Sie gemäß einigen der anderen Antworten etwas mit dem Sortieren tun. Für den allgemeinen Fall von Arrays beliebiger Objekte glaube ich nicht, dass Sie es auf lange Sicht vermeiden können. Im Folgenden erhalten Sie den Schnittpunkt einer beliebigen Anzahl von Arrays, die als Parameter für Folgendes bereitgestellt werden 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"]; 
Tim Down
quelle
Dies funktioniert nur in dem Fall, in dem die Objektidentität die einzige Form der Gleichheit ist.
Steven Huwig
Nun ja, aber ich denke, das scheint den meisten Menschen natürlich zu sein. Es ist auch trivial, eine alternative Funktion anzuschließen, um einen anderen Gleichheitstest durchzuführen.
Tim Down
Ich denke, Sie erstellen in Ihrem Beispiel versehentlich eine globale Variable firstArr.
Jason Jackson
@ JasonJackson: Du hast recht, danke. Klar meine Meinung geändert , ob die Variable rufen firstArroder firstArrayund nicht alle Verweise aktualisieren. Fest.
Tim Down
7

Mit ES2015 und Sets ist es ziemlich kurz. Akzeptiert Array-ähnliche Werte wie einen String und entfernt Duplikate.

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

SeregPie
quelle
Konvertieren von Set in Array mit [... a] entfernt die doppelten Elemente, gute Idee, danke
V-SHY
1
Diese Lösung wurde zweimal vor Ihrer bereitgestellt.
Madbreaks
7

Sie könnten eine Verwendung Setals thisArgvon Array#filterund nehmen Set#hasals Rückruf.

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

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

Nina Scholz
quelle
Ich weiß nicht, warum dies nicht mehr Stimmen hat. Es ist eindeutig die beste Antwort.
Paul Rooney
5

Eine winzige Änderung an der kleinsten hier (der Filter / IndexOf-Lösung ), nämlich das Erstellen eines Index der Werte in einem der Arrays mithilfe eines JavaScript-Objekts, reduziert diese von O (N * M) auf "wahrscheinlich" lineare Zeit. Quelle1 Quelle2

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

Dies ist weder die einfachste Lösung (es ist mehr Code als filter + indexOf ) noch die schnellste (wahrscheinlich um einen konstanten Faktor langsamer als intersect_safe () ), scheint aber eine ziemlich gute Balance zu sein. Es ist sehr einfach, bietet aber eine gute Leistung und erfordert keine vorsortierten Eingaben.

David
quelle
5

Ein weiterer indizierter Ansatz, der eine beliebige Anzahl von Arrays gleichzeitig verarbeiten kann:

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

Es funktioniert nur für Werte, die als Zeichenfolgen ausgewertet werden können, und Sie sollten sie als Array wie folgt übergeben:

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

... aber es akzeptiert Objekte transparent als Parameter oder als eines der zu schneidenden Elemente (wobei immer ein Array gemeinsamer Werte zurückgegeben wird). Beispiele:

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

EDIT: Ich habe gerade bemerkt, dass dies in gewisser Weise etwas fehlerhaft ist.

Das heißt: Ich habe es codiert, weil ich dachte, dass Eingabearrays selbst keine Wiederholungen enthalten können (wie im angegebenen Beispiel nicht).

Wenn Eingabearrays jedoch Wiederholungen enthalten, führt dies zu falschen Ergebnissen. Beispiel (unter Verwendung der folgenden Implementierung):

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

Glücklicherweise lässt sich dies leicht beheben, indem einfach die Indizierung der zweiten Ebene hinzugefügt wird. Das ist:

Veränderung:

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

durch:

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

...und:

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

durch:

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

Vollständiges Beispiel:

// 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' ]
Bitifet
quelle
2
Dies ist bei weitem die beste Antwort mit einer kleinen Modifikation. Nach dem var v = Hinzufügen der Zeile if (typeof v == 'function') continue;wird das Hinzufügen von Funktionen zu den Ergebnissen übersprungen. Vielen Dank!
Zsolti
Danke @Zsolti. Ich füge Ihren Vorschlag nicht hinzu, da Funktionen (und die Art und Weise, wie wir damit umgehen möchten) nicht in den Rahmen der ursprünglichen Frage fallen. Aber siehe meine Bearbeitung: Wenn Sie Wiederholungen in Ihren Eingabearrays haben können, ist die ursprüngliche Implementierung fehlerhaft. Ich habe es in meiner Bearbeitung behoben. ... Wenn Sie jedoch sicher sind, dass es keine Wiederholungen gibt, ist die ursprüngliche Implementierung etwas billiger.
Bitifet
... über Funktionen können sie auch geschnitten werden: Wenn wir sie erkennen, wie @Zsolti sagt (mit if (typeof v == 'function'), dann können wir seine Stringifikation ( v.toString()) als Schlüssel für den Index verwenden. Aber wir müssen etwas tun, um ihn intakt zu halten Am einfachsten ist es, einfach die ursprüngliche Funktion als Wert anstelle eines einfachen booleschen wahren Werts zuzuweisen. In diesem Fall sollte jedoch auch die neueste Deindexierung geändert werden, um diesen Zustand zu erkennen und den richtigen Wert (die Funktion) wiederherzustellen.
Bitifet
Wie schnell kann das mit 30 Arrays mit 100 Elementen sein. Wie ist die CPU-Auslastung?
Aidonsnous
5

Sie können verwenden (für alle Browser außer IE):

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

oder für IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);
jota3
quelle
Wäre schön , wenn man das in eine Funktion drehen konnte
avalanche1
@ avalanche1 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;
}
Gabe
quelle
4

Mit einigen Einschränkungen für Ihre Daten können Sie dies linear tun Zeit !

Für positive Ganzzahlen : Verwenden Sie ein Array, das die Werte einem Booleschen Wert "gesehen / nicht gesehen" zuordnet.

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

Es gibt eine ähnliche Technik für Objekte : Nehmen Sie einen Dummy-Schlüssel, setzen Sie ihn für jedes Element in Array1 auf "true" und suchen Sie diesen Schlüssel in Elementen von Array2. Räumen Sie auf, wenn Sie fertig sind.

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

Natürlich müssen Sie sicher sein, dass der Schlüssel vorher nicht angezeigt wurde, sonst zerstören Sie Ihre Daten ...

Tarulen
quelle
Übrigens kann dies leicht erweitert werden, um eine beliebige Anzahl von Arrays zu schneiden: Ersetzen Sie den Booleschen Wert durch Ganzzahlen und erhöhen Sie ihn jedes Mal, wenn er angezeigt wird: Sie können den Schnittpunkt in der letzten Runde leicht lesen.
Tarulen
Interessante Lösung, ich mag es. Die meisten anderen Lösungen sind O (n ^ 2), aber dies ist O (n). Ich habe den Integer-Code hier zu jicfiddle.net/321juyLu/2 hinzugefügt . Es kam 3., ich mag :)
rmcsharry
3

Ich werde mit dem beitragen, was für mich am besten funktioniert hat:

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;
};
}
Johan
quelle
3

"indexOf" für IE 9.0, Chrome, Firefox, Oper,

    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]
Martin Roberto Mondragon Sotel
quelle
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))

Vlad Bezden
quelle
2

Ein funktionaler Ansatz mit ES2015

Ein funktionaler Ansatz muss in Betracht ziehen, nur reine Funktionen ohne Nebenwirkungen zu verwenden, von denen jede nur einen einzelnen Job betrifft.

Diese Einschränkungen verbessern die Zusammensetzbarkeit und Wiederverwendbarkeit der beteiligten Funktionen.

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

Bitte beachten Sie, dass der native SetTyp verwendet wird, der eine vorteilhafte Suchleistung bietet.

Vermeiden Sie Duplikate

Offensichtlich Arraybleiben wiederholt vorkommende Elemente aus dem ersten erhalten, während das zweite nicht Arraydupliziert wird. Dies kann das gewünschte Verhalten sein oder auch nicht. Wenn Sie ein eindeutiges Ergebnis benötigen, wenden Sie einfach dedupedas erste Argument an:

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

Berechnen Sie den Schnittpunkt einer beliebigen Anzahl von Arrays

Wenn Sie den Schnittpunkt einer beliebigen Anzahl von Arrays berechnen möchten, komponieren Sie einfach intersectmit foldl. Hier ist eine Komfortfunktion:

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


quelle
Beeindruckend funktionell: Ich musste eine doppelte Aufnahme machen, um zu bestätigen, dass dies nicht Haskell ist. Der einzige Trottel ist: (expr ? true : false)ist redundant. Verwenden Sie nur, exprwenn keine tatsächlichen Booleschen Werte benötigt werden, sondern nur "wahr" / "falsch".
Jose_castro_arnaud
2

Der Einfachheit halber:

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

Leistungen:

  • Schmutz einfach
  • datenzentriert
  • funktioniert für eine beliebige Anzahl von Listen
  • funktioniert für beliebig lange Listen
  • funktioniert für beliebige Wertetypen
  • funktioniert für beliebige Sortierreihenfolge
  • behält die Form bei (Reihenfolge des ersten Auftretens in einem beliebigen Array)
  • fährt nach Möglichkeit früh ab
  • speichersicher, ohne Manipulationen an Funktions- / Array-Prototypen

Nachteile:

  • höhere Speichernutzung
  • höhere CPU-Auslastung
  • erfordert ein Verständnis von reduzieren
  • erfordert Verständnis des Datenflusses

Sie möchten dies nicht für 3D-Engine- oder Kernel-Arbeiten verwenden, aber wenn Sie Probleme haben, dies in einer ereignisbasierten App auszuführen, hat Ihr Design größere Probleme.

Norguard
quelle
2

.reduceum eine Karte zu erstellen und .filterdie Kreuzung zu finden. deleteInnerhalb von .filterkönnen wir das zweite Array so behandeln, als wäre es eine eindeutige Menge.

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

Ich finde diesen Ansatz ziemlich einfach zu überlegen. Es arbeitet in konstanter Zeit.

Belden
quelle
2

Dies ist neben list1.filter (n => list2.includes (n)) wahrscheinlich die einfachste.

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

Chris Lwin
quelle
Dies hat O (nm) Zeitkomplexität ... Dies könnte in O (n + m)
Alchuang
2

Wenn es mehrere Arrays schneiden muss:

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]

Belfordz
quelle
Aber wie schnell ist diese Lösung für 30 Arrays mit 100 Elementen?
Aidonsnous
Hierbei werden nur native Methoden für Javascript verwendet. Daher kann die VM, auf der der Code ausgeführt wird, ihn so weit wie möglich optimieren. Ich bin mir ziemlich sicher, dass es keine schnellere Lösung gibt, da Sie dies in einer modernen Version von V8 im Verhältnis zum Alter dieses Kommentars ausführen.
Belfordz
2

ES6-Stil auf einfache Weise.

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

Beispiel:

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

Ich habe eine Schnittfunktion geschrieben, die sogar Schnittpunkte von Arrays von Objekten basierend auf bestimmten Eigenschaften dieser Objekte erkennen kann.

Zum Beispiel,

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

und wir wollen eine Schnittmenge basierend auf der idEigenschaft, dann sollte die Ausgabe sein:

[{id: 20}]

Daher lautet die Funktion für dasselbe (Hinweis: ES6-Code):

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

und Sie können die Funktion aufrufen als:

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

Beachten Sie auch: Diese Funktion findet Schnittpunkte, wenn das erste Array das primäre Array ist und das Schnittpunktergebnis daher das des primären Arrays ist.

Mridul Meharia
quelle
2

Denken Sie, dass dies mit der Zeit O (Array1 + Array2) schneller sein wird, vorausgesetzt, map.has () ist ~ O (1). Bitte korrigieren Sie mich, wenn Sie falsch liegen.

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

ajaix
quelle
1

Hier ist die Implementierung von 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;
};

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

Dorian
quelle
Keine schlechte Referenz, wenn Undesrcore verfügbar ist
Dimitrios Mistriotis