查找多个JavaScript数组之间的匹配

我有多个string值的数组,我想比较它们,只保留所有这些匹配结果相同。

给出这个例子代码:

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

我想要产生以下数组,其中包含来自所有给定数组的匹配:

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

我知道我可以结合所有的数组与var newArr = arr1.concat(arr2, arr3); 但是这只是给了我一个arrays,加上重复的东西。 这可以轻松完成,而不需要像underscore.js这样的库的开销?

(太好了, 现在我也饿了!)

编辑我想我应该提到,可能有一个未知量的数组,我只是用3作为一个例子。

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

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

您可以首先sorting外部数组以获得最短的数组…

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

为了完整起见,这里有一个解决scheme,在数组中处理重复。 它使用.filter()而不是.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; }, []); 

演示: http : //jsfiddle.net/nWjcp/4/

现在,您已经为问题添加了不确定数量的数组,这是另一种方法,它将每个项目的计数收集到一个对象中,然后整理具有最大数量的项目。

这种方法的优点:

  1. 如果数组较大,那么蛮力search选项(由其他答案使用)的速度要快15倍
  2. 不需要ES5或ES5垫片(适用于所有浏览器)
  3. 完全无损(根本不改变源数据)
  4. 处理源数组中的项目
  5. 处理任意数量的input数组

这里是代码:

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

工作演示: http : //jsfiddle.net/jfriend00/52mAP/

仅供参考,这不需要ES5,所以在没有垫片的情况下,所有的浏览器都能正常工作。

在每个1000个长度的15个数组的性能testing中,这比使用的search方法快了10倍以上,我不是在这个jsperf中的答案: http ://jsperf.com/in-all-arrays。


这是一个使用ES6 MapSet去重复和跟踪计数的版本。 这有一个好处,即数据的types被保留下来,并且可以是任何东西(甚至不需要进行自然的string转换,数据甚至可以是对象,尽pipe对象被比较为完全相同的对象,不具有相同的对象属性/值)。

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

假设有一个我们想要find交集的数组,我们可以使用一个最简单的单线程方法

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

几个想法 – 你可以比较最短的数组中的项目,并防止在返回的数组中重复。

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

返回值: apple,fish,pizza

DEMOhttp://jsfiddle.net/kMcud/

假设数组的数组并检查所有数组:

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

这应该适用于任何数量的数组:

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

这是一个单线解决scheme。 你可以把它分成两个思路:

  1. 计算两个数组之间的连接/交集
 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}`); 

只是为了这个,另一个长长的手段:

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

编辑

在Object.prototype上find基于thise的永远不可枚举的属性的函数:

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

这本质上是所有答案的汇编:

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