Question

Quel est le code le plus simple et sans bibliothèque pour implémenter les intersections du tableau en JavaScript? Je veux ecrire

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

et obtenir

[2, 3]
Était-ce utile?

La solution

Utiliser une combinaison de Array.prototype.filter et Array.prototype.indexOf:

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

Ou comme vroupel suggéré dans les commentaires, vous pouvez utiliser le plus récent Array.prototype.includes pour un code encore plus simple:

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

Pour les navigateurs plus âgés:

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

Autres conseils

Destructor semble le plus simple, surtout si nous pouvons 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;
}

Le non destructif doit être un cheveux 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;
}

Si votre environnement prend en charge Ecmascript 6 Set, une manière simple et soi-disant efficace (voir le 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 un nouveau Set de b à chaque fois:

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

Notez que lorsque vous utilisez des ensembles, vous n'obtiendrez que des valeurs distinctes, donc new Set[1,2,3,3].size évalue à 3.

Utilisant Souligner.js ou lodash.js

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

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

Que diriez-vous de simplement utiliser 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;
}

Les performances de l'implémentation de @ Atk pour les tableaux triés de primitives peuvent être améliorés 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 en utilisant jsperf: http://bit.ly/p9frzk. Il est environ trois fois plus rapide d'utiliser .pop.

// 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 inférieure succincte qui surpasse les autres implémentations sur les grandes entrées. Si les performances sur les petites entrées sont importantes, vérifiez les alternatives ci-dessous.

Alternatives et comparaison des performances:

Voir l'extrait suivant pour des implémentations alternatives et vérifier https://jsperf.com/array-intection-Combarison pour les comparaisons de performance.

function intersect_for(a, b) {
  const result = [];
  const alen = a.length;
  const blen = b.length;
  for (let i = 0; i < alen; ++i) {
    const ai = a[i];
    for (let j = 0; j < blen; ++j) {
      if (ai === b[j]) {
        result.push(ai);
        break;
      }
    }
  } 
  return result;
}

function intersect_filter_indexOf(a, b) {
  return a.filter(el => b.indexOf(el) !== -1);
}

function intersect_filter_in(a, b) {
  const map = b.reduce((map, el) => {map[el] = true; return map}, {});
  return a.filter(el => el in map);
}

function intersect_for_in(a, b) {
  const result = [];
  const map = {};
  for (let i = 0, length = b.length; i < length; ++i) {
    map[b[i]] = true;
  }
  for (let i = 0, length = a.length; i < length; ++i) {
    if (a[i] in map) result.push(a[i]);
  }
  return result;
}

function intersect_filter_includes(a, b) {
  return a.filter(el => b.includes(el));
}

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

function intersect_filter_has_arrow(a, b) {
  const set = new Set(b);
  return a.filter(el => set.has(el));
}

function intersect_for_has(a, b) {
  const result = [];
  const set = new Set(b);
  for (let i = 0, length = a.length; i < length; ++i) {
    if (set.has(a[i])) result.push(a[i]);
  }
  return result;
}

Résultats dans Firefox 53:

  • OPS / SEC sur les grandes 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
    

Utilisant jquery:

var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);
  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 des tableaux d'objets arbitaires peut ne pas fonctionner.

Pour les tableaux ne contenant que 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 de loin. Ce qui suit vous donnera l'intersection de tout nombre de tableaux fournis comme paramètres à 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"]; 

Il est assez court en utilisant ES2015 et les ensembles. Accepte des 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(''));

Un petit ajustement au plus petit ici (le Solution de filtre / index), à savoir la création d'un indice 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 filtre + index), ni le plus rapide (probablement plus lent par un facteur constant que intersect_safe ()), mais semble être un assez bon équilibre. C'est sur le très Side simple, tout en offrant de bonnes performances, et il ne nécessite pas d'entrées pré-triées.

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

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

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

... mais il accepte de manière transparente les objets comme paramètre ou comme n'importe lequel des éléments à intersecture (le tableau toujours de retour 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"]

ÉDITER: Je viens de remarquer que c'est, en quelque sorte, légèrement buggy.

Autrement dit: je l'ai codé en pensant que les tableaux d'entrée ne peuvent pas contenir lui-même des répétitions (comme l'exemple fourni ne le fait pas).

Mais si les tableaux d'entrée contiennent des répétitions, cela produirait de mauvais résultats. Exemple (en utilisant la mise en œuvre ci-dessous):

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

Heureusement, cela est facile à corriger en ajoutant simplement l'indexation de deuxième niveau. C'est-à-dire:

Changer:

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

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

Pour entiers positifs: Utilisez un tableau mappant les valeurs dans 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 objets: Prenez une clé factice, définissez-la sur "vrai" pour chaque élément de Array1, puis recherchez cette clé dans les éléments de l'arraie2. Nettoyez lorsque 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 être sûr que la clé n'apparaissait pas auparavant, sinon vous détruire vos données ...

Je vais contribuer à 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;
};
}

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

C'est probablement le plus simple, en plus list1.filter (n => list2. y compris (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"]

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

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

Une approche fonctionnelle avec ES2015

Une approche fonctionnelle doit envisager uniquement d'utiliser des fonctions pures sans effets secondaires, dont chacun ne concerne que 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 natif Set Le type est utilisé, qui a une performance de recherche avantageuse.

Évitez les doublons

Évidemment, il se produisait à plusieurs reprises des éléments du premier Array sont conservés, tandis que le second Array est déduisé. Cela peut être ou peut être le comportement souhaité. Si vous avez besoin d'un résultat unique, appliquez simplement dedupe au 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 souhaitez calculer l'intersection d'un nombre arbitraire de Arrays juste composer intersect avec foldl. Voici une fonction de commodité:

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

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
  • axé sur les données
  • Fonctionne pour un nombre arbitraire de listes
  • Fonctionne pour des longueurs arbitraires de listes
  • Fonctionne pour des types de valeurs arbitraires
  • Fonctionne pour une commande de tri arbitraire
  • conserve la forme (ordre de la première apparence dans n'importe quel tableau)
  • sort tôt dans la mesure du possible
  • Sécurité de la mémoire, à court de falsification de prototypes de fonction / tableau

Désavantages:

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

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

.reduce Pour construire une carte, et .filter pour trouver l'intersection. delete dans .filter nous 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.

Voici souligner.js la mise en oeuvre:

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

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

function getIntersection(arr1, arr2){
    var result = [];
    arr1.forEach(function(elem){
        arr2.forEach(function(elem2){
            if(elem === elem2){
                result.push(elem);
            }
        });
    });
    return result;
}

getIntersection([1,2,3], [2,3,4,5]); // [ 2, 3 ]

Si vous avez besoin de le faire gérer plusieurs tableaux multiples:

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]

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

Plutôt en utilisant l'index que vous pouvez également utiliser Array.protype. y compris.

function intersection(arr1, arr2) {
  return arr1.filter((ele => {
    return arr2.includes(ele);
  }));
}

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

Vous n'avez pas besoin de déclarer une variable intermédiaire à l'intérieur de la fonction pour le deuxième tableau si le deuxième tableau va toujours être géré comme un Positionner.

La solution suivante renvoie un tableau de valeurs uniques qui se produisent dans les deux tableaux:

const intersection = (a, b) => {
  b = new Set(b); // recycling variable
  return [...new Set(a)].filter(e => b.has(e));
};

console.log(intersection([1, 2, 3, 1, 1], [1, 2, 4])); // Array [ 1, 2 ]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top