查找多个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/
现在,您已经为问题添加了不确定数量的数组,这是另一种方法,它将每个项目的计数收集到一个对象中,然后整理具有最大数量的项目。
这种方法的优点:
- 如果数组较大,那么蛮力search选项(由其他答案使用)的速度要快15倍
- 不需要ES5或ES5垫片(适用于所有浏览器)
- 完全无损(根本不改变源数据)
- 处理源数组中的项目
- 处理任意数量的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 Map
和Set
去重复和跟踪计数的版本。 这有一个好处,即数据的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
DEMO – http://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。 你可以把它分成两个思路:
- 计算两个数组之间的连接/交集
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>