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]

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

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

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.


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]

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


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

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

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.


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.


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

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

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


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


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.


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

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

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

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


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

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]

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

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


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.


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.


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


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]


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]

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.


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


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


Keine schlechte Referenz, wenn Undesrcore verfügbar ist
Dimitrios Mistriotis
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.