Code le plus simple pour l'intersection de tableaux en javascript

611

Quel est le code le plus simple et sans bibliothèque pour implémenter les intersections de tableaux en javascript? Je veux ecrire

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

et obtenir

[2, 3]
Peter
la source
16
Voulez-vous simple ou rapide?
SLaks
11
La priorité est simple, mais elle ne peut pas être si mortelle que ce sera un porc de performance :)
Peter
4
J'ai créé une page de test JsFiddle Banchmark pour toutes les méthodes ici, y compris lafonction d' intersection _underscore . (plus c'est mieux) ! entrez la description de l'image ici Jusqu'à présent, intersect_safe a donné les meilleurs résultats . VOUS & Soulignez le pire.
neoswf
L'ajout d'un breakà Simple js loopsaugmente les ops / sec à ~ 10M
Richard
19
Au cas où vous le manqueriez : la réponse la plus simple n'est pas celle acceptée mais celle du bas: stackoverflow.com/questions/1885557/…
redben

Réponses:

1081

Utilisez une combinaison de Array.prototype.filteret Array.prototype.indexOf:

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

Ou comme suggéré par vrugtehagel dans les commentaires, vous pouvez utiliser le plus récent Array.prototype.includespour un code encore plus simple:

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

Pour les navigateurs plus anciens:

array1.filter(function(n) {
    return array2.indexOf(n) !== -1;
});
Anon.
la source
9
Ce que vous pouvez résoudre en ajoutant une version de bibliothèque sur le prototype de la baie.
Anon.
12
Oui, mais cela valait la peine d'être mentionné.
Tim Down
18
Meilleure réponse ici, à la fois pour la simplicité et pour travailler avec des non-nombres
Muhd
41
intersection([1,2,1,1,3], [1])retourne [1, 1, 1]. Cela ne devrait-il pas revenir juste [1]?
edjroot
21
Au lieu d' array2.indexOf(n) != -1un, vous pouvez également écrire array2.includes(n)pour un code encore plus simple.
vrugtehagel
157

Destructif semble le plus simple, surtout si l'on peut supposer que l'entrée est triée:

/* 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 destructif doit être un cheveu plus compliqué, car nous devons suivre les indices:

/* 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
la source
14
Il y a de nombreuses erreurs dans intersect_safe: lengthest une propriété dans les tableaux, pas une méthode. Il y a une variable non repoussée idans result.push(a[i]);. Enfin, cela ne fonctionne tout simplement pas dans le cas général: deux objets où ni l'un ni l'autre n'est supérieur à l'autre selon l' >opérateur ne sont pas nécessairement égaux. intersect_safe( [ {} ], [ {} ] ), par exemple, donnera (une fois les erreurs mentionnées ci-dessus corrigées) un tableau avec un élément, ce qui est clairement faux.
Tim Down
1
@Tim Down: correction des erreurs de syntaxe que vous avez signalées. Que ce soit correct ou incorrect de considérer quoi que ce soit ni plus ni moins comme égal dépend des exigences. Je ne remarque rien dans la question d'origine disant que le contenu devrait contenir des tableaux. Maintenant, vous auriez raison de dire qu'une entrée inattendue doit être gérée, mais si la spécification dicte déjà que l'entrée doit être un tableau de nombres (comme je l'ai supposé), le code conviendra.
ATK
1
@atk: Je prends votre point de vue, vu que l'exemple de la question utilise des tableaux qui ne contiennent que des nombres.
Tim Down
4
Vous pouvez également utiliser .slice(0)pour créer un clone du tableau dans intersect_safe, plutôt que de suivre les index.
johnluetke
1
@thesmart: vous avez raison, il existe certainement des façons plus performantes de le faire. Le code, ci-dessus, devait être simple, pas rapide :)
atk
59

Si votre environnement prend en charge ECMAScript 6 Set , une manière simple et supposée efficace (voir lien de spécification):

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

Plus court, mais moins lisible (également sans créer l'intersection supplémentaire Set):

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

Éviter une nouvelle Setde bchaque fois:

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

Notez que lors de l' utilisation des ensembles vous obtenez seulement des valeurs distinctes, donc new Set[1,2,3,3].sizeévalue à 3.

nbarbosa
la source
1
quelle est cette [...setA]syntaxe? Un type particulier d'opération javascript?
jxramos
1
@jxramos qui est la syntaxe Spread et dans ce cas, il est juste utilisé pour créer un tableau à partir des éléments de l'ensemble. "Array.from (setA)" fonctionnerait également dans ce cas, mais comme la question demandait "la plus simple", j'ai essayé de rendre la lecture plus propre sur cette ligne. À ce sujet, je pense que le code pourrait être plus simple, donc je mettrai à jour l'extrait de code.
nbarbosa
@nbarbosa Je suis curieux: pourquoi avez-vous "cloné" le tableau a? #filter ne détruit pas le tableau d'origine, non? Il crée un nouveau tableau?
bplittle
@bplittle Je viens de créer un tableau de l'ensemble afin de supprimer les doublons, sinon, l'utilisation directe du tableau entraînerait le renvoi de doublons. Par exemple, si j'utilisais directement le tableau, intersect ([1,2,2,4], [2,3]) donnerait [2, 2].
nbarbosa
2
Cette x => new Set(b).has(x)fonction de flèche ne se transforme- t-elle pas ben ensemble à chaque exécution? Vous devriez probablement enregistrer cet ensemble dans une variable.
Aran-Fey
39

Utilisation de Underscore.js ou lodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]
Sai Ram
la source
20
Op a demandé "sans bibliothèque".
LinuxDisciple
@LinuxDisciple Mon erreur d'avoir manqué ça.Merci pour les notes
Sai Ram
33
Dans tous les cas, il s'agit de la meilleure liste Google pour cette recherche, il est donc utile d'avoir une réponse de la bibliothèque. Merci.
webnoob
Moi aussi, je suis content que cela ait été publié. La première fois, j'ai ressenti le besoin de underscore.js. Habituellement, la cartographie JavaScript et la réduction des pipelines font le travail avec élégance, mais pas cette fois.
Sridhar Sarnobat
I <3 Underscore et I <3 Jeremy Ashkenas (son créateur), mais malgré cela, je recommande fortement de consulter Lodash à la place. C'est fondamentalement une version supérieure d'Underscore (c'était à l'origine une fourchette) dont le seul inconvénient est le code source super-optimisé (et donc presque illisible). Les gens d'Underscore ont même envisagé de se débarrasser complètement d'Underscore (et de simplement dire aux gens d'utiliser Lodash), mais les gens qui se soucient de la lisibilité du code source ont fait valoir de le garder (j'étais de ce côté en fait, mais je me suis depuis converti en Lodash). @see github.com/jashkenas/underscore/issues/2182
machineghost
14

Ma contribution en termes ES6. En général, il trouve l'intersection d'un tableau avec un nombre indéfini de tableaux fournis comme arguments.

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

Redu
la source
ce code a fière allure, mais je ne le comprends pas complètement. Possible de l'expliquer s'il vous plait?
theusual
1
@novembersky Il rassemble tous les tableaux de sujets dans un tableau similaire [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7],[4,6]], puis s'applique .reduce(). La première [0,1,2,3,4,5,6,7,8,9].filter( e => [0,2,4,6,8].includes(e)opération est effectuée et le résultat devient le nouveau pet cdevient [4,5,6,7]au tour suivant et continue ainsi jusqu'à ce qu'il n'en creste plus .
Redu
1
Il s'agit d'une solution coûteuse si vous travaillez avec de grands ensembles de données.
Madbreaks
1
Ne modifiez pas prototypeinutilement.
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]));

Je recommande la solution succincte ci-dessus qui surpasse les autres implémentations sur de grandes entrées. Si les performances sur les petites entrées sont importantes, vérifiez les alternatives ci-dessous.

Alternatives et comparaison des performances:

Consultez l'extrait de code suivant pour d'autres implémentations et consultez https://jsperf.com/array-intersection-comparison pour des comparaisons de performances.

Résultats dans Firefox 53:

  • Ops / sec sur les grands tableaux (10 000 éléments):

    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 sur de petits tableaux (100 éléments):

    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
la source
2
intersect([1,2,2,3], [2,3,4,5])retourne [2, 2, 3].
SeregPie
1
@SeregPie Exactement. Selon le commentaire "Renvoyer les éléments du tableau a qui sont également en b"
le_m
Réponse de qualité, cependant, l'utilisation de Sets modifie fondamentalement les résultats puisque la question de l'op ne porte que sur les intersections de tableaux et ne fait aucune mention / stipulation sur la façon de gérer les doublons. Timide, cette réponse peut donner des résultats inattendus lorsqu'il existe des doublons.
Madbreaks
1
J'adore, mais vous avez ajouté une fonction inutile avec "filtre + inclut". essayez a.filter(b.includes). Il devrait fonctionner beaucoup plus rapidement (comme pour la mise à niveau de votre fonction).
SEoF
11

Que diriez-vous d'utiliser simplement des tableaux associatifs?

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

Éditer:

// 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
la source
1
Cela n'a de chance que si vos tableaux contiennent uniquement des chaînes ou des nombres, et si aucun des scripts de votre page n'a été endommagé Object.prototype.
Tim Down
2
L'exemple de l'OP utilisait des nombres, et si un script a gâché avec Object.prototype, alors le script doit être réécrit ou supprimé.
Steven Huwig
Vous n'avez pas besoin des deux (d1) et (d2). Créez (d2), puis parcourez (a) au lieu de boucler (d1).
StanleyH
Devrait être d[b[i]] = true;au lieu de d[b[j]] = true;( inon j). Mais l'édition nécessite 6 caractères.
Izhaki
@Izhaki thanks, fixed. (Ajout d'un // commentaire pour contourner les exigences minimales de modification.)
Steven Huwig
8
  1. Trier
  2. vérifiez un par un à partir de l'index 0, créez un nouveau tableau à partir de cela.

Quelque chose comme ça, pas bien testé cependant.

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'algorithme destiné uniquement aux nombres et aux chaînes normales, l'intersection de tableaux d'objets arbitraires peut ne pas fonctionner.

VOUS
la source
3
Le tri ne sera pas nécessairement utile pour les tableaux d'objets arbitraires
Tim Down
Si le tableau n'est pas trié, vous devez boucler environ 1 000 000 fois lorsque vous intersectez un tableau de 1000 longueurs x un tableau de 1000 longueurs
VOUS
Je pense que vous avez manqué mon point de vue, à savoir que les objets arbitraires en JavaScript n'ont pas d'ordre de tri naturel, ce qui signifie que le tri d'un tableau d'objets arbitraires n'entraînera pas le regroupement d'objets égaux. Ce n'est pas bon d'avoir un algorithme efficace qui ne fonctionne pas.
Tim Down
Ah désolé, j'ai raté des "objets arbitraires", oui, vous avez raison. ces objets ne peuvent pas les trier et l'algorithme peut ne pas fonctionner sur ceux-ci.
VOUS
8

Les performances de l'implémentation de @ atk pour les tableaux triés de primitives peuvent être améliorées en utilisant .pop plutôt que .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;
}

J'ai créé une référence à l'aide de jsPerf: http://bit.ly/P9FrZK . Il est environ trois fois plus rapide d'utiliser .pop.

xn.
la source
1
Juste comme note secondaire pour les autres - cela ne fonctionnera que pour les nombres, pas pour les chaînes.
Izhaki
Notez que si vous remplacez a[aLast] > b[bLast]par a[aLast].localeCompare(b[bLast]) > 0(et la même chose que else ifci - dessous), cela fonctionnera sur les chaînes.
Andrew
1
La différence de vitesse dépend de la taille des tableaux car .popest O (1) et .shift()est O (n)
Esailija
8

Utilisation de jQuery :

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);
Gowsikan
la source
8
Cela pourrait également être écrit sous la forme c = $(b).filter(a);, mais je ne recommanderais pas de compter sur jQuery pour ce type de manipulation de tableau, car la documentation mentionne uniquement que cela fonctionne pour les éléments.
Stryner
1
Cela ne répond pas à la question d'op: "Quel est le code le plus simple, sans bibliothèque ..."
Madbreaks
7

Pour les tableaux contenant uniquement des chaînes ou des nombres, vous pouvez faire quelque chose avec le tri, selon certaines des autres réponses. Pour le cas général des tableaux d'objets arbitraires, je ne pense pas que vous puissiez éviter de le faire à long terme. Ce qui suit vous donnera l'intersection de n'importe quel nombre de tableaux fournis comme paramètres pour 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
la source
Cela ne fonctionne que dans le cas où l'identité d'objet est la seule forme d'égalité.
Steven Huwig
Eh bien oui, mais je pense que c'est ce qui semblerait naturel à la plupart des gens. Il est également trivial de brancher une fonction alternative pour effectuer un test d'égalité différent.
Tim Down
Je pense que vous créez accidentellement une variable globale firstArr dans votre exemple.
Jason Jackson
@JasonJackson: Vous avez raison, merci. J'ai clairement changé d'avis sur le fait d'appeler la variable firstArrou de firstArrayne pas mettre à jour toutes les références. Fixé.
Tim Down
7

C'est assez court en utilisant ES2015 et Sets. Accepte les valeurs de type tableau comme une chaîne et supprime les doublons.

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
la source
Convertir de Set en tableau avec [... a] supprimera les éléments en double, bonne idée, merci
V-SHY
1
Cette solution a été fournie deux fois avant la vôtre.
Madbreaks
7

Vous pouvez utiliser un SetasthisArg de Array#filteret prendre Set#hascomme rappel.

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
la source
Je ne sais pas pourquoi cela n'a pas obtenu plus de votes. C'est clairement la meilleure réponse.
Paul Rooney
5

Un petit ajustement au plus petit ici (la solution filter / indexOf ), à savoir la création d'un index des valeurs dans l'un des tableaux à l'aide d'un objet JavaScript, le réduira de O (N * M) à un temps linéaire "probablement". source1 source2

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

Ce n'est pas la solution la plus simple (c'est plus de code que filter + indexOf ), ni la plus rapide (probablement plus lente d'un facteur constant que intersect_safe () ), mais cela semble être un assez bon équilibre. Il est très simple, tout en offrant de bonnes performances, et il ne nécessite pas d'entrées pré-triées.

David
la source
5

Une autre approche indexée capable de traiter n'importe quel nombre de tableaux à la fois:

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

Cela ne fonctionne que pour les valeurs qui peuvent être évaluées en tant que chaînes et vous devez les passer sous forme de tableau comme:

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

... mais il accepte de manière transparente les objets comme paramètre ou comme l'un des éléments à recouper (renvoyant toujours un tableau de valeurs communes). Exemples:

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

EDIT: Je viens de remarquer que c'est, en quelque sorte, légèrement buggé.

C'est-à-dire: je l'ai codé en pensant que les tableaux d'entrée ne peuvent pas contenir de répétitions (comme l'exemple fourni ne le fait pas).

Mais si les tableaux d'entrée contiennent des répétitions, cela produira des résultats erronés. Exemple (en utilisant l'implémentation ci-dessous):

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

Heureusement, cela est facile à résoudre en ajoutant simplement une indexation de second niveau. C'est:

Changement:

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

par:

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

...et:

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

par:

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

Exemple complet:

// 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
la source
2
C'est de loin la meilleure réponse avec une petite modification. Après la var v = ligne, ajoutez if (typeof v == 'function') continue;et cela sautera l'ajout de fonctions aux résultats. Merci!
Zsolti
Merci @Zsolti. Je n'ajoute pas votre suggestion car avoir des fonctions (et la façon dont nous voulons les gérer) est hors de portée de la question d'origine. Mais voyez ma modification: si vous pouvez avoir des répétitions dans vos tableaux d'entrée, alors l'implémentation d'origine est boguée. Je l'ai corrigé dans mon montage. ... D'un autre côté, si vous savez avec certitude qu'il n'y aura pas de répétition, l'implémentation d'origine est légèrement moins chère.
bitifet du
... sur les fonctions, elles peuvent également être intersectées: si nous les détectons comme le dit @Zsolti (avec if (typeof v == 'function'), alors nous pouvons utiliser sa stringification ( v.toString()) comme clé pour l'index. Mais, nous devons faire quelque chose pour le conserver intact. la manière la plus simple de le faire est simplement d'affecter la fonction d'origine en tant que valeur au lieu d'une simple valeur booléenne vraie. Mais, dans ce cas, la dernière désindexation doit également être modifiée pour détecter cette condition et restaurer la bonne valeur (la fonction).
bitifet
À quelle vitesse cela peut-il être avec 30 baies avec 100 éléments .. comment est l'utilisation du processeur?
aidonsnous
5

Vous pouvez utiliser (pour tous les navigateurs sauf IE):

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

ou pour IE:

const intersection = array1.filter(element => array2.indexOf(element) !== -1);
jota3
la source
Ce serait bien si vous pouviez transformer cela en une fonction
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
la source
4

Avec quelques restrictions sur vos données, vous pouvez le faire en temps linéaire !

Pour les entiers positifs : utilisez un tableau mappant les valeurs à un booléen "vu / non vu".

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

Il existe une technique similaire pour les objets : prenez une clé factice, définissez-la sur "true" pour chaque élément de array1, puis recherchez cette clé dans les éléments de array2. Nettoyez quand vous avez terminé.

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

Bien sûr, vous devez vous assurer que la clé n'apparaissait pas auparavant, sinon vous allez détruire vos données ...

tarulen
la source
Soit dit en passant, cela peut être facilement étendu pour intersecter n'importe quel nombre de tableaux: remplacez le booléen par des entiers, et incrémentez chaque fois qu'il est vu: vous pouvez facilement lire l'intersection du dernier tour.
tarulen
Solution intéressante, j'aime ça. La plupart des autres solutions sont O (n ^ 2), mais c'est O (n). J'ai ajouté le code entier au violon de performance d' ericP ici jsfiddle.net/321juyLu/2 . Il est arrivé 3e, j'aime :)
rmcsharry
3

Je contribuerai avec ce qui a fonctionné le mieux pour moi:

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
la source
3

"indexOf" pour 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]
Martin Roberto Mondragon Sotel
la source
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
la source
2

Une approche fonctionnelle avec ES2015

Une approche fonctionnelle doit envisager d'utiliser uniquement des fonctions pures sans effets secondaires, dont chacune ne concerne qu'un seul travail.

Ces restrictions améliorent la composabilité et la réutilisabilité des fonctions impliquées.

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

Veuillez noter que le Settype natif est utilisé, ce qui présente des performances de recherche avantageuses.

Évitez les doublons

Les éléments apparaissant de manière répétée du premier Arraysont évidemment conservés, tandis que le second Arrayest dédoublonné. Cela peut être ou ne pas être le comportement souhaité. Si vous avez besoin d'un résultat unique, appliquez simplement dedupele premier argument:

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

Calculer l'intersection de n'importe quel nombre de Arrays

Si vous voulez calculer l'intersection d'un nombre arbitraire de Arrays composez simplement intersectavec foldl. Voici une fonction pratique:

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


la source
Impressionnant fonctionnel: a dû faire une double prise pour confirmer que ce n'est pas Haskell. Le seul nitpick est: (expr ? true : false)est redondant. Utilisez juste exprsi les booléens réels ne sont pas nécessaires, juste véridiques / fausses.
jose_castro_arnaud
2

Pour la simplicité:

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

Avantages:

  • saleté simple
  • centré sur les données
  • fonctionne pour un nombre arbitraire de listes
  • fonctionne pour des longueurs arbitraires de listes
  • fonctionne pour les types de valeurs arbitraires
  • fonctionne pour l'ordre de tri arbitraire
  • conserve sa forme (ordre de première apparition dans n'importe quel tableau)
  • sort tôt si possible
  • sans danger pour la mémoire, sans altération des prototypes de fonctions / matrices

Désavantages:

  • utilisation de la mémoire plus élevée
  • utilisation du processeur plus élevée
  • nécessite une compréhension de réduire
  • nécessite une compréhension du flux de données

Vous ne voudriez pas l'utiliser pour le moteur 3D ou le travail du noyau, mais si vous avez des problèmes pour que cela fonctionne dans une application basée sur des événements, votre conception a de plus gros problèmes.

Norguard
la source
2

.reducepour construire une carte et .filtertrouver l'intersection. deletedans le .filternous permet de traiter le deuxième tableau comme s'il s'agissait d'un ensemble unique.

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

Je trouve cette approche assez facile à raisonner. Il fonctionne en temps constant.

Belden
la source
2

C'est probablement le plus simple, en plus de 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"]

Chris Lwin
la source
cela a une complexité temporelle O (nm) ... cela pourrait être résolu dans O (n + m)
alchuang
2

Si vous devez le faire gérer plusieurs tableaux entrecroisés:

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
la source
Mais à quelle vitesse est cette solution pour 30 baies avec 100 éléments?
aidonsnous
Cela n'utilise rien d'autre que des méthodes natives de Javascript, et la machine virtuelle dans laquelle le code s'exécutera est donc libre de l'optimiser autant que possible. Je suis tout à fait certain qu'il n'existe pas de solution plus rapide étant donné que vous exécutez cela dans une version moderne de V8 par rapport à l'âge de ce commentaire.
Belfordz
2

Manière simple de style ES6.

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

Exemple:

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

J'ai écrit une fonction d'intersection qui peut même détecter l'intersection d'un tableau d'objets en fonction de la propriété particulière de ces objets.

Par exemple,

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

et nous voulons une intersection basée sur la idpropriété, alors la sortie devrait être:

[{id: 20}]

En tant que tel, la fonction pour le même (note: code ES6) est:

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

et vous pouvez appeler la fonction comme:

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

Notez également: cette fonction trouve une intersection étant donné que le premier tableau est le tableau principal et donc le résultat de l'intersection sera celui du tableau principal.

Mridul Meharia
la source
2

Pensez que ce sera plus rapide avec le temps O (array1 + array2) en supposant que map.has () est ~ O (1). Veuillez me corriger en cas d'erreur.

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
la source
1

Voici l' implémentation 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;
};

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

Dorian
la source
Pas une mauvaise référence si Undesrcore est disponible
Dimitrios Mistriotis