查找多个 JavaScript 数组之间的匹配项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11076067/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Finding matches between multiple JavaScript Arrays
提问by CBarr
I have multiple arrays with string values and I want to compare them and only keep the matching results that are identical between ALLof them.
我有多个带有字符串值的数组,我想比较它们并且只保留所有数组之间相同的匹配结果。
Given this example code:
鉴于此示例代码:
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 to produce the following array that contains matches from all given arrays:
我想生成以下包含来自所有给定数组的匹配项的数组:
['apple', 'fish', 'pizza']
I know I can combine all the arrays with var newArr = arr1.concat(arr2, arr3);
but that just give me an array with everything, plus the duplicates. Can this be done easily without needing the overhead of libraries such as underscore.js?
我知道我可以将所有数组组合在一起,var newArr = arr1.concat(arr2, arr3);
但这只是给我一个包含所有内容的数组,再加上重复项。这可以在不需要 underscore.js 等库的开销的情况下轻松完成吗?
(Great, and nowi'm hungry too!)
(太好了,现在我也饿了!)
EDITI suppose I should mention that there could be an unknown amount of arrays, I was just using 3 as an example.
编辑我想我应该提到可能有未知数量的数组,我只是以 3 为例。
回答by
var result = arrays.shift().filter(function(v) {
return arrays.every(function(a) {
return a.indexOf(v) !== -1;
});
});
DEMO:http://jsfiddle.net/nWjcp/2/
演示:http ://jsfiddle.net/nWjcp/2/
You could first sort the outer Array to get the shortest Array at the beginning...
您可以先对外部 Array 进行排序以在开始时获得最短的 Array ......
arrays.sort(function(a, b) {
return a.length - b.length;
});
For completeness, here's a solution that deals with duplicates in the Arrays. It uses .reduce()
instead of .filter()
...
为了完整起见,这里有一个处理数组中重复项的解决方案。它使用.reduce()
代替.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;
}, []);
回答by Redu
Assuming there is an array of arrays those we want to find the intersection of, a 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>");
回答by jfriend00
Now, that you've added an indeterminate number of arrays to the question, here's another approach that collects the count for each item into an object and then collates the items that have the max count.
现在,您已经向问题添加了不确定数量的数组,这是另一种方法,将每个项目的计数收集到一个对象中,然后整理具有最大计数的项目。
Advantages of this approach:
这种方法的优点:
- ~15x faster that brute force search options (used by other answers) if arrays are larger
- Does not require ES5 or ES5 shim (works with all browsers)
- Completely non-destructive (doesn't change source data at all)
- Handles duplicates items in source arrays
- Handles an arbitrary number of input arrays
- 如果数组更大,则蛮力搜索选项(由其他答案使用)快 15 倍
- 不需要 ES5 或 ES5 垫片(适用于所有浏览器)
- 完全无损(根本不改变源数据)
- 处理源数组中的重复项
- 处理任意数量的输入数组
And here's 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/
工作演示:http: //jsfiddle.net/jfriend00/52mAP/
FYI, this does not require ES5 so will work in all browsers without a shim.
仅供参考,这不需要 ES5,因此可以在没有垫片的所有浏览器中使用。
In a performance test on 15 arrays each 1000 long, this was more than 10x faster than the search method used in am not i am's answer in this jsperf: http://jsperf.com/in-all-arrays.
在对每 1000 个长度的 15 个数组进行的性能测试中,这比在这个 jsperf 中使用的搜索方法快 10 倍以上:http://jsperf.com/in-all-arrays 。
Here's a version that uses an ES6 Map
and Set
to de-dup and keep track of counts. This has the advantage that the type of data is preserved and can be anything (it doesn't even have to have a natural string conversion, the data can even be objects though objects are compared for being the exact same object, not having the same properties/values).
这是一个使用 ES6Map
并Set
进行重复数据删除和跟踪计数的版本。这具有保留数据类型并且可以是任何类型的优点(它甚至不必进行自然的字符串转换,数据甚至可以是对象,尽管将对象比较为完全相同的对象,而不具有相同的对象)属性/值)。
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>";
回答by kennebec
A couple thoughts- you can compare just the items in the shortest array, and prevent duplicates 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();
returned value: apple,fish,pizza
返回值: apple,fish,pizza
回答by charlietfl
Assuming array of arrays and checking through all arrays:
假设数组数组并检查所有数组:
DEMO: http://jsfiddle.net/qUQHW/
演示: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;
})
回答by dinigo
Here goes a single-line solution. You can split it into two thinking steps:
这是一个单行解决方案。你可以把它分成两个思考步骤:
- Calculate join/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}`);
- Reduce the content: calculate the intersection between the acumulated 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}`);
回答by md_salm
// The easiest way!!
var arr1 = ['apple', 'orange', 'banana', 'pear', 'fish', 'pancake', 'taco', 'pizza'];
var arr2 = ['taco', 'fish', 'apple', 'pizza'];
var arr3 = ['banana', 'pizza', 'fish', 'apple'];
var arr4 = [];
for(let i of arr1){
if(arr2.includes(i) && arr3.includes(i)){
arr4.push(i)
}
}
console.log(arr4)
回答by Blender
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);
回答by RobG
Just for the heck of it, another long hand 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'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']
];
Edit
编辑
Function to find never enumerable properties based on thise on Object.prototype:
在 Object.prototype 上基于 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>'));
回答by bob
This is essentially a compilation of all the answers boiled down:
这基本上是所有答案的汇编:
// 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>