web-dev-qa-db-fra.com

Recherche de correspondances entre plusieurs tableaux JavaScript

J'ai plusieurs tableaux avec des valeurs de chaîne et je veux les comparer et ne garder que les résultats de correspondance qui sont identiques entreTOUSd'entre eux.

Étant donné cet exemple de code:

var arr1 = ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'Apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'Apple'];

Je voudrais produire le tableau suivant qui contient les correspondances de tous les tableaux donnés:

['Apple', 'fish', 'pizza']

Je sais que je peux combiner tous les tableaux avec var newArr = arr1.concat(arr2, arr3); mais cela me donne juste un tableau avec tout, plus les doublons. Cela peut-il être fait facilement sans avoir besoin des frais généraux des bibliothèques telles que underscore.js?

(Génial et maintenant j'ai faim aussi!)

EDITJe suppose que je devrais mentionner qu’il pourrait y avoir un nombre inconnu de tableaux, j’utilisais simplement 3 comme exemple.

50
Chris Barr
var result = arrays.shift().filter(function(v) {
    return arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    });
});

DEMO:http://jsfiddle.net/nWjcp/2/

Vous pouvez d'abord trier le tableau extérieur pour obtenir le tableau le plus court au début ...

arrays.sort(function(a, b) {
    return a.length - b.length;
});

Pour être complet, voici une solution qui traite les doublons dans les tableaux. Il utilise .reduce() au lieu de .filter()...

var result = arrays.shift().reduce(function(res, v) {
    if (res.indexOf(v) === -1 && arrays.every(function(a) {
        return a.indexOf(v) !== -1;
    })) res.Push(v);
    return res;
}, []);

DEMO:http://jsfiddle.net/nWjcp/4/

80
user1106925

Maintenant que vous avez ajouté un nombre indéterminé de tableaux à la question, voici une autre approche qui collecte le nombre de chaque élément dans un objet, puis associe les éléments dont le nombre est maximal.

Avantages de cette approche:

  1. ~ 15 fois plus rapide que les options de recherche de force brute (utilisées par d'autres réponses) si les tableaux sont plus grands
  2. Ne nécessite pas de shim ES5 ou ES5 (fonctionne avec tous les navigateurs)
  3. Complètement non destructif (ne change pas du tout les données source)
  4. Gère les éléments en double dans les tableaux sources
  5. Gère un nombre arbitraire de tableaux d'entrée

Et voici le code:

function containsAll(/* pass all arrays here */) {
    var output = [];
    var cntObj = {};
    var array, item, cnt;
    // for each array passed as an argument to the function
    for (var i = 0; i < arguments.length; i++) {
        array = arguments[i];
        // for each element in the array
        for (var j = 0; j < array.length; j++) {
            item = "-" + array[j];
            cnt = cntObj[item] || 0;
            // if cnt is exactly the number of previous arrays, 
            // then increment by one so we count only one per array
            if (cnt == i) {
                cntObj[item] = cnt + 1;
            }
        }
    }
    // now collect all results that are in all arrays
    for (item in cntObj) {
        if (cntObj.hasOwnProperty(item) && cntObj[item] === arguments.length) {
            output.Push(item.substring(1));
        }
    }
    return(output);
}    

Démo de travail: http://jsfiddle.net/jfriend00/52mAP/

Pour votre information, cela ne nécessite pas ES5 et fonctionnera donc dans tous les navigateurs sans cale.

Lors d'un test de performances effectué sur 15 baies de disques de 1 000 fois chacune, il était plus de 10 fois plus rapide que la méthode de recherche utilisée dans la réponse de am not am am: http://jsperf.com/in-all-arrays .


Voici une version qui utilise une ES6 Map et Set pour dédoubler et garder une trace des comptes. Cela présente l'avantage que le type de données est préservé et peut être n'importe quoi (il n'est même pas nécessaire d'avoir une conversion de chaîne naturelle, les données peuvent même être des objets bien que les objets soient comparés pour être exactement le même objet, n'ayant pas le même propriétés/valeurs).

var arrays = [
    ['valueOf', 'toString','Apple', 'orange', 'banana', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza', 1, 2, 999, 888],
    ['valueOf', 'toString','taco', 'fish', 'fish', 'Apple', 'pizza', 1, 999, 777, 999, 1],
    ['valueOf', 'toString','banana', 'pizza', 'fish', 'Apple', 'Apple', 1, 2, 999, 666, 555]
    ];
    
// subclass for updating cnts    
class MapCnt extends Map {
    constructor(iterable) {
        super(iterable);
    }
    
    cnt(iterable) {
        // make sure items from the array are unique
        let set = new Set(iterable);
        // now update the cnt for each item in the set
        for (let item of set) {
            let cnt = this.get(item) || 0;
            ++cnt;
            this.set(item, cnt);
        }
    }
}


function containsAll(...allArrays) {
    let cntObj = new MapCnt();
    for (array of allArrays) {
        cntObj.cnt(array);
    }
    // now see how many items have the full cnt
    let output = [];
    for (var [item, cnt] of cntObj.entries()) {
        if (cnt === allArrays.length) {
            output.Push(item);
        }
    }
    return(output);
}    

var result = containsAll.apply(this, arrays);

document.body.innerHTML = "<pre>[<br>    " + result.join(',<br>    ') + "<br>]</pre>";

12
jfriend00

En supposant qu'il y ait un tableau de tableaux ceux dont nous voulons trouver l'intersection, une approche simple plus simple pourrait être

var arr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5,6,7]],
    int = arr.reduce((p,c) => p.filter(e => c.includes(e)));

document.write("<pre>" + JSON.stringify(int) + "</pre>");

9
Redu

Quelques réflexions - vous pouvez comparer uniquement les éléments du tableau le plus court,

function arraysInCommon(arrays){
    var i, common,
    L= arrays.length, min= Infinity;
    while(L){
        if(arrays[--L].length<min){
            min= arrays[L].length;
            i= L;
        }
    }
    common= arrays.splice(i, 1)[0];
    return common.filter(function(itm, indx){
        if(common.indexOf(itm)== indx){
            return arrays.every(function(arr){
                return arr.indexOf(itm)!= -1;
            });
        }
    });
}

var arr1= ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2= ['taco', 'fish', 'Apple', 'pizza', 'Apple','Apple'];
var arr3= ['banana', 'pizza', 'fish', 'Apple','fish'];

var allArrays = [arr1,arr2,arr3];

arraysInCommon(allArrays).sort();

valeur renvoyée: Apple,fish,pizza

DEMO - http://jsfiddle.net/kMcud/

3
kennebec

Voici une solution à une seule ligne. Vous pouvez le scinder en deux étapes de réflexion:

  1. Calculer la jointure/intersection entre deux tableaux

var arrA = [1,2,3,4,5];
var arrB = [4,5,10];
var innerJoin = arrA.filter(el=>arrB.includes(el));
console.log(`Intersection is: ${innerJoin}`);

  1. Réduisez le contenu: calculez l'intersection entre l'intersection cumulée et le tableau suivant.

var arrays = [
 ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'],
 ['taco', 'fish', 'Apple', 'pizza'],
 ['banana', 'pizza', 'fish', 'Apple']
];
var join = arrays.reduce((join, current) => join.filter(el => current.includes(el)));
console.log(`Intersection is: ${join}`);

2
dinigo

En supposant un tableau de tableaux et en vérifiant tous les tableaux:

DEMO: http://jsfiddle.net/qUQHW/

var tmp = {};
for (i = 0; i < data.length; i++) {
    for (j = 0; j < data[i].length; j++) {
        if (!tmp[data[i][j]]) {
            tmp[data[i][j]] = 0;
        }
        tmp[data[i][j]]++;
    }
}

var results = $.map(tmp, function(val,key) {
    return val == data.length ? key :null;
})
2
charlietfl

Cela devrait fonctionner pour n'importe quel nombre de tableaux:

function intersection(arr1, arr2) {
  var temp = [];

  for (var i in arr1) {
    var element = arr1[i];

    if (arr2.indexOf(element) > -1) {
      temp.Push(element);
    }
  }

  return temp;
}

function multi_intersect() {
  var arrays = Array.prototype.slice.apply(arguments).slice(1);
  var temp = arguments[0];

  for (var i in arrays) {
    temp = intersection(arrays[i], temp);

    if (temp == []) {
      break;
    }
  }

  return temp;
}

var arr1 = ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'Apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'Apple'];

multi_intersect(arr1, arr2, arr3);
1
Blender

Ceci est essentiellement une compilation de toutes les réponses résumées:

	 // Intersect any number of arrays:

	function intersect() {

	  // - Arguments -> traditional array,
	  // - First item ( arrays[0] ) = shortest to reduce iterations
	  var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
	    return a.length - b.length;
	  });

	  // Use first array[0] as the base.
	  var a = arrays.shift();

	  var result = [];
	  for (var i = a.length; i--;) {

	    var val = a[i];

	    // Prevent duplicates
	    if (result.indexOf(val) < 0) {

	      // Seek
	      var found = true;
	      for (var ii = arrays.length; ii--;) {
	        if (arrays[ii].indexOf(val) < 0) {
	          found = false;
	          break;
	        }
	      }

	      if (found) {
	        result.Push(val);
	      }

	    }

	  }

	  return result;

	}

	/*
	// Slower, but smaller code-base:
	function intersect (){
		
		// - Arguments -> traditional array,
		// - First item ( arrays[0] ) = shortest to reduce iterations
		var arrays = Array.prototype.slice.call(arguments).sort(function(a, b) {
	        return a.length - b.length;
	    });
		
		// Use first array[0] as the base.
		var a = arrays.shift();

		return a.filter(function (val, idx, aa) {
			
						// Seek
		                for(var i=arrays.length; i--;){
		                    if (arrays[i].indexOf(val) < 0) {
							    return false;
						    }
		                }
						
						// Prevent duplicates
		                return aa.indexOf(val) === idx;
		
					});

	}
	*/

	var arr1 = ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
	var arr2 = ['taco', 'fish', 'Apple', 'pizza', 'Apple', 'Apple'];
	var arr3 = ['banana', 'pizza', 'fish', 'Apple', 'fish'];

	var arr1 = ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
	var arr2 = ['taco', 'fish', 'Apple', 'pizza', 'Apple', 'Apple'];
	var arr3 = ['banana', 'pizza', 'fish', 'Apple', 'fish'];


	var result = intersect(arr1, arr2, arr3);

	 // For fiddle output:
	var elem = document.getElementById("result");
	elem.innerHTML = JSON.stringify(result);
	console.log(result);
<div id="result">Results</div>

0
bob

Juste pour le plaisir, une autre approche à main longue:

function getCommon(a) {

  // default result is copy of first array
  var result = a[0].slice();
  var mem, arr, found = false;

  // For each member of result, see if it's in all other arrays
  // Go backwards so can splice missing entries
  var i = result.length;

  while (i--) {
    mem = result[i];

    // Check in each array
    for (var j=1, jLen=a.length; j<jLen; j++) {
      arr = a[j];
      found = false;

      // For each member of arr and until found
      var k = arr.length;
      while (k-- && !found) {

        // If found in this array, set found to true
        if (mem == arr[k]) {
          found = true;
        }
      }
      // if Word wasn't found in this array, remove it from result and 
      // start on next member of result, skip remaining arrays.
      if (!found) {
        result.splice(i,1);
        break;
      }
    }
  }
  return result;
}

var data = [
  ['taco', 'fish', 'Apple', 'pizza', 'mango', 'pear'],
  ['Apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'],
  ['banana', 'pizza', 'fish', 'Apple'],
  ['banana', 'pizza', 'fish', 'Apple', 'mango', 'pear']
];

Modifier

Fonction pour ne jamais trouver de propriétés énumérables basées sur thise sur Object.prototype:

// Return an array of Object.prototype property names that are not enumerable
// even when added directly to an object.
// Can be helpful with IE as properties like toString are not enumerable even
// when added to an object.
function getNeverEnumerables() {

    // List of Object.prototype property names plus a random name for testing
    var spNames = 'constructor toString toLocaleString valueOf ' +
                  'hasOwnProperty isPrototypeOf propertyIsEnumerable foo';

    var spObj = {foo:'', 'constructor':'', 'toString':'', 'toLocaleString':'', 'valueOf':'',
                 'hasOwnProperty':'', 'isPrototypeOf':'', 'propertyIsEnumerable':''};

    var re = [];

    // BUild list of enumerable names in spObj
    for (var p in spObj) {
      re.Push(p); 
    }

    // Remove enumerable names from spNames and turn into an array
    re = new RegExp('(^|\\s)' + re.join('|') + '(\\s|$)','g');
    return spNames.replace(re, ' ').replace(/(^\s+)|\s\s+|(\s+$)/g,'').split(' ');
}

document.write(getNeverEnumerables().join('<br>'));
0
RobG