Finding matches between multiple JavaScript arrays

I have several arrays with string values, and I want to compare them, and only matching results match ALL between them.

Given this code example:

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

I would like to create the following array containing matches from all given arrays:

 ['apple', 'fish', 'pizza'] 

I know that I can combine all arrays with var newArr = arr1.concat(arr2, arr3); but it just gives me an array with everything plus duplicates. Can this be done easily without the overhead of libraries such as underscore.js?

(Great, and now I'm hungry too!)

EDIT Suppose I have to mention that there may be an unknown number of arrays, as an example I used only 3.

+44
javascript jquery arrays
Jun 18 '12 at 1:23
source share
9 answers
 var result = arrays.shift().filter(function(v) { return arrays.every(function(a) { return a.indexOf(v) !== -1; }); }); 

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

First you can sort the external array to get the shortest array at the beginning ...

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



For completeness, here is a solution that concerns duplicates in arrays. It uses .reduce() instead of .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/

+70
Jun 18 '12 at 1:29
source share

Now that you have added an indefinite number of arrays to this question, here is another approach that collects the count for each element into an object and then matches the elements with the maximum number.

The advantages of this approach:

  • ~ 15 times faster than brute force search parameters (used by other answers) if arrays are larger
  • Does not require ES5 or ES5 laying (works with all browsers)
  • Completely non-destructive (does not change the source data at all)
  • Handles duplicate elements in source arrays
  • Handles an arbitrary number of input arrays

And here is the 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); } 

Working demo: http://jsfiddle.net/jfriend00/52mAP/

FYI, this does not require ES5, so it will work in all browsers without padding.

In a performance test on 15 arrays, each of which lasted 1000, this was more than 10 times faster than the search method used by am japerf: http://jsperf.com/in-all-arrays .




Here's a version that uses ES6 Map and Set to delete and track samples. This has the advantage that the data type is saved and can be anything (it does not even have to have a natural string conversion, the data can even be objects, although objects are compared in order to be the same object without the same property / value )

 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>"; 
+10
Jun 18 '12 at 1:30
source share

Assuming there is an array of arrays that we want to find by intersection, the simplest single-liner approach could be

 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>"); 
+6
May 6 '16 at 7:46 a.m.
source share

A few thoughts - you can only compare elements in the shortest array, and prevent duplication in the returned array.

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

return value: apple,fish,pizza

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

+3
Jun 18
source share

Assuming an array of arrays and checking all arrays:

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
Jun 18 '12 at 2:00
source share

Here is a one line solution. You can divide it into two stages:

  • Calculate union / intersection between two arrays

 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. Decrease contents: calculate the intersection between the calculated intersection and the next array.

 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
Nov 03 '17 at 12:31 on
source share

This should work for any number of arrays:

 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
Jun 18 2018-12-18T00:
source share

Just for this, another long approach:

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

Edit

A function for finding never-enumerated properties based on this.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
Jun 18 '12 at 3:12
source share

This, in essence, is a compilation of all the answers that fell:

  // 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
May 19 '16 at 8:19
source share



All Articles