Simplest code for array intersection in javascript
在javascript中实现数组交叉的最简单,无库的代码是什么? 我想写
1 | intersection([1,2,3], [2,3,4,5]) |
得到
1 | [2, 3] |
使用
1 | array1.filter(value => -1 !== array2.indexOf(value)) |
或者正如vrugtehagel在评论中建议的那样,你可以使用更新的
1 | array1.filter(value => array2.includes(value)) |
对于旧浏览器:
1 2 3 | array1.filter(function(n) { return array2.indexOf(n) !== -1; }); |
破坏性似乎最简单,特别是如果我们可以假设输入已排序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /* destructively finds the intersection of * two arrays in a simple fashion. * * PARAMS * a - first array, must already be sorted * b - second array, must already be sorted * * NOTES * State of input arrays is undefined when * the function returns. They should be * (prolly) be dumped. * * Should have O(n) operations, where n is * n = MIN(a.length, b.length) */ function intersection_destructive(a, b) { var result = []; while( a.length > 0 && b.length > 0 ) { if (a[0] < b[0] ){ a.shift(); } else if (a[0] > b[0] ){ b.shift(); } else /* they're equal */ { result.push(a.shift()); b.shift(); } } return result; } |
非破坏性必须是一个更复杂的头发,因为我们必须跟踪索引:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /* finds the intersection of * two arrays in a simple fashion. * * PARAMS * a - first array, must already be sorted * b - second array, must already be sorted * * NOTES * * Should have O(n) operations, where n is * n = MIN(a.length(), b.length()) */ function intersect_safe(a, b) { var ai=0, bi=0; var result = []; while( ai < a.length && bi < b.length ) { if (a[ai] < b[bi] ){ ai++; } else if (a[ai] > b[bi] ){ bi++; } else /* they're equal */ { result.push(a[ai]); ai++; bi++; } } return result; } |
如果您的环境支持ECMAScript 6 Set,那么一种简单且有效的(参见规范链接)方式:
1 2 3 4 5 6 | function intersect(a, b) { var setA = new Set(a); var setB = new Set(b); var intersection = new Set([...setA].filter(x => setB.has(x))); return Array.from(intersection); } |
更短,但可读性更低(也没有创建额外的交集
1 2 3 | function intersect(a, b) { return [...new Set(a)].filter(x => new Set(b).has(x)); } |
每次从
1 2 3 4 | function intersect(a, b) { var setB = new Set(b); return [...new Set(a)].filter(x => setB.has(x)); } |
请注意,使用集合时,您将只获得不同的值,因此
使用Underscore.js或lodash.js
1 | _.intersection( [0,345,324] , [1,0,324] ) // gives [0,324] |
我在ES6方面的贡献。通常,它会找到一个数组的交集,该数组具有作为参数提供的无限数量的数组。
1 2 3 4 5 6 7 | Array.prototype.intersect = function(...a) { return [this,...a].reduce((p,c) => p.filter(e => c.includes(e))); } var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]], arr = [0,1,2,3,4,5,6,7,8,9]; document.write("[cc lang="javascript"]" + JSON.stringify(arr.intersect(...arrs)) +" |
"); 代码> PRE>
如何使用关联数组呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function intersect(a, b) { var d1 = {}; var d2 = {}; var results = []; for (var i = 0; i < a.length; i++) { d1[a[i]] = true; } for (var j = 0; j < b.length; j++) { d2[b[j]] = true; } for (var k in d1) { if (d2[k]) results.push(k); } return results; } |
编辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // new version function intersect(a, b) { var d = {}; var results = []; for (var i = 0; i < b.length; i++) { d[b[i]] = true; } for (var j = 0; j < a.length; j++) { if (d[a[j]]) results.push(a[j]); } return results; } |
使用jQuery:
1 2 3 4 | var a = [1,2,3]; var b = [2,3,4,5]; var c = $(b).not($(b).not(a)); alert(c); |
通过使用.pop而不是.shift,可以改善@ atk对基元排序数组的实现性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function intersect(array1, array2) { var result = []; // Don't destroy the original arrays var a = array1.slice(0); var b = array2.slice(0); var aLast = a.length - 1; var bLast = b.length - 1; while (aLast >= 0 && bLast >= 0) { if (a[aLast] > b[bLast] ) { a.pop(); aLast--; } else if (a[aLast] < b[bLast] ){ b.pop(); bLast--; } else /* they're equal */ { result.push(a.pop()); b.pop(); aLast--; bLast--; } } return result; } |
我使用jsPerf创建了一个基准:http://bit.ly/P9FrZK。它的使用速度快了三倍.pop。
1 2 3 4 5 6 7 | // Return elements of array a that are also in b in linear time: function intersect(a, b) { return a.filter(Set.prototype.has, new Set(b)); } // Example: console.log(intersect([1,2,3], [2,3,4,5])); |
我推荐以上简洁的解决方案,它在大输入上优于其他实现。如果小投入的表现很重要,请查看以下备选方案。
替代方案和性能比较:
有关替代实施的信息,请参阅以下代码段,并检查https://jsperf.com/array-intersection-comparison以进行性能比较。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | function intersect_for(a, b) { const result = []; const alen = a.length; const blen = b.length; for (let i = 0; i < alen; ++i) { const ai = a[i]; for (let j = 0; j < blen; ++j) { if (ai === b[j]) { result.push(ai); break; } } } return result; } function intersect_filter_indexOf(a, b) { return a.filter(el => b.indexOf(el) !== -1); } function intersect_filter_in(a, b) { const map = b.reduce((map, el) => {map[el] = true; return map}, {}); return a.filter(el => el in map); } function intersect_for_in(a, b) { const result = []; const map = {}; for (let i = 0, length = b.length; i < length; ++i) { map[b[i]] = true; } for (let i = 0, length = a.length; i < length; ++i) { if (a[i] in map) result.push(a[i]); } return result; } function intersect_filter_includes(a, b) { return a.filter(el => b.includes(el)); } function intersect_filter_has_this(a, b) { return a.filter(Set.prototype.has, new Set(b)); } function intersect_filter_has_arrow(a, b) { const set = new Set(b); return a.filter(el => set.has(el)); } function intersect_for_has(a, b) { const result = []; const set = new Set(b); for (let i = 0, length = a.length; i < length; ++i) { if (set.has(a[i])) result.push(a[i]); } return result; } |
Firefox 53中的结果:
-
大型阵列上的Ops / sec(10,000个元素):
1
2
3
4
5
6
7filter + has (this) 523 (this answer)
for + has 482
for-loop + in 279
filter + in 242
for-loops 24
filter + includes 14
filter + indexOf 10 -
小数组上的Ops / sec(100个元素):
1
2
3
4
5
6
7for-loop + in 384,426
filter + in 192,066
for-loops 159,137
filter + includes 104,068
filter + indexOf 71,598
filter + has (this) 43,531 (this answer)
filter + has (arrow function) 35,588
像这样的东西,虽然测试不好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function intersection(x,y){ x.sort();y.sort(); var i=j=0;ret=[]; while(i<x.length && j<y.length){ if(x[i]<y[j])i++; else if(y[j]<x[i])j++; else { ret.push(x[i]); i++,j++; } } return ret; } alert(intersection([1,2,3], [2,3,4,5])); |
PS:该算法仅适用于Numbers和Normal Strings,仲裁对象数组的交集可能不起作用。
对于仅包含字符串或数字的数组,您可以根据其他一些答案对排序执行某些操作。对于任意对象数组的一般情况,我认为你不能避免长期使用它。以下将为您提供作为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | var arrayContains = Array.prototype.indexOf ? function(arr, val) { return arr.indexOf(val) > -1; } : function(arr, val) { var i = arr.length; while (i--) { if (arr[i] === val) { return true; } } return false; }; function arrayIntersection() { var val, arrayCount, firstArray, i, j, intersection = [], missing; var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array // Search for common values firstArray = arrays.pop(); if (firstArray) { j = firstArray.length; arrayCount = arrays.length; while (j--) { val = firstArray[j]; missing = false; // Check val is present in each remaining array i = arrayCount; while (!missing && i--) { if ( !arrayContains(arrays[i], val) ) { missing = true; } } if (!missing) { intersection.push(val); } } } return intersection; } arrayIntersection( [1, 2, 3,"a"], [1,"a", 2], ["a", 1] ); // Gives [1,"a"]; |
使用ES2015和Sets非常简短。接受类似于String的类似数组的值并删除重复项。
1 2 3 4 5 6 7 8 | let intersection = function(a, b) { a = new Set(a), b = new Set(b); return [...a].filter(v => b.has(v)); }; console.log(intersection([1,2,1,2,3], [2,3,5,4,5,3])); console.log(intersection('ccaabbab', 'addb').join('')); |
对这里最小的一个微调(filter / indexOf解决方案),即使用JavaScript对象在其中一个数组中创建值的索引,将从O(N * M)减少到"可能"的线性时间。 source1 source2
1 2 3 4 5 | function intersect(a, b) { var aa = {}; a.forEach(function(v) { aa[v]=1; }); return b.filter(function(v) { return v in aa; }); } |
这不是最简单的解决方案(它比filter + indexOf更多的代码),也不是最快的(可能是一个常数因子比intersect_safe()更慢),但似乎是一个非常好的平衡。它非常简单,同时提供良好的性能,并且不需要预先排序的输入。
另一种能够同时处理任意数量数组的索引方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // Calculate intersection of multiple array or object values. function intersect (arrList) { var arrLength = Object.keys(arrList).length; // (Also accepts regular objects as input) var index = {}; for (var i in arrList) { for (var j in arrList[i]) { var v = arrList[i][j]; if (index[v] === undefined) index[v] = 0; index[v]++; }; }; var retv = []; for (var i in index) { if (index[i] == arrLength) retv.push(i); }; return retv; }; |
它仅适用于可以作为字符串计算的值,您应该将它们作为数组传递,如:
1 | intersect ([arr1, arr2, arr3...]); |
...但它透明地接受对象作为参数或任何要交叉的元素(总是返回公共值的数组)。例子:
1 2 | intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4] intersect ([{x:"hello", y:"world"}, ["hello","user"]]); // ["hello"] |
编辑:我刚刚注意到,这在某种程度上是略有错误。
那就是:我编码认为输入数组本身不能包含重复(如提供的示例所示)。
但是如果输入数组恰好包含重复,那么会产生错误的结果。示例(使用以下实现):
1 2 3 | intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // Expected: [ '1' ] // Actual: [ '1', '3' ] |
幸运的是,只需添加第二级索引即可轻松解决此问题。那是:
更改:
1 2 | if (index[v] === undefined) index[v] = 0; index[v]++; |
通过:
1 2 | if (index[v] === undefined) index[v] = {}; index[v][i] = true; // Mark as present in i input. |
...和:
1 | if (index[i] == arrLength) retv.push(i); |
通过:
1 | if (Object.keys(index[i]).length == arrLength) retv.push(i); |
完整的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Calculate intersection of multiple array or object values. function intersect (arrList) { var arrLength = Object.keys(arrList).length; // (Also accepts regular objects as input) var index = {}; for (var i in arrList) { for (var j in arrList[i]) { var v = arrList[i][j]; if (index[v] === undefined) index[v] = {}; index[v][i] = true; // Mark as present in i input. }; }; var retv = []; for (var i in index) { if (Object.keys(index[i]).length == arrLength) retv.push(i); }; return retv; }; intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ] |
由于您的数据有一些限制,您可以在线性时间内完成!
对于正整数:使用将值映射到"看到/未看到"布尔值的数组。
1 2 3 4 5 6 7 8 9 10 11 12 | function intersectIntegers(array1,array2) { var seen=[], result=[]; for (var i = 0; i < array1.length; i++) { seen[array1[i]] = true; } for (var i = 0; i < array2.length; i++) { if ( seen[array2[i]]) result.push(array2[i]); } return result; } |
对象有一种类似的技术:获取一个虚拟键,为array1中的每个元素设置为"true",然后在array2的元素中查找该键。完成后清理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function intersectObjects(array1,array2) { var result=[]; var key="tmpKey_intersect" for (var i = 0; i < array1.length; i++) { array1[i][key] = true; } for (var i = 0; i < array2.length; i++) { if (array2[i][key]) result.push(array2[i]); } for (var i = 0; i < array1.length; i++) { delete array1[i][key]; } return result; } |
当然你需要确保密钥没有出现,否则你将破坏你的数据......
1 2 3 4 5 6 7 8 9 10 11 | function intersection(A,B){ var result = new Array(); for (i=0; i<A.length; i++) { for (j=0; j<B.length; j++) { if (A[i] == B[j] && $.inArray(A[i],result) == -1) { result.push(A[i]); } } } return result; } |
这可能是最简单的一个,
除了list1.filter(n => list2.includes(n))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var list1 = ['bread', 'ice cream', 'cereals', 'strawberry', 'chocolate'] var list2 = ['bread', 'cherry', 'ice cream', 'oats'] function check_common(list1, list2){ list3 = [] for (let i=0; i<list1.length; i++){ for (let j=0; j<list2.length; j++){ if (list1[i] === list2[j]){ list3.push(list1[i]); } } } return list3 } check_common(list1, list2) // ["bread","ice cream"] |
我会为最适合我的事情做出贡献:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | if (!Array.prototype.intersect){ Array.prototype.intersect = function (arr1) { var r = [], o = {}, l = this.length, i, v; for (i = 0; i < l; i++) { o[this[i]] = true; } l = arr1.length; for (i = 0; i < l; i++) { v = arr1[i]; if (v in o) { r.push(v); } } return r; }; } |
您可以使用(对于IE以外的所有浏览器):
1 | const intersection = array1.filter(element => array2.includes(element)); |
或IE:
1 | const intersection = array1.filter(element => array2.indexOf(element) !== -1); |
"indexOf"适用于IE 9.0,chrome,firefox,opera,
1 2 3 4 5 6 7 8 | function intersection(a,b){ var rs = [], x = a.length; while (x--) b.indexOf(a[x])!=-1 && rs.push(a[x]); return rs.sort(); } intersection([1,2,3], [2,3,4,5]); //Result: [2,3] |
ES2015的功能方法
功能方法必须考虑仅使用没有副作用的纯函数,每个函数仅涉及单个作业。
这些限制增强了所涉及功能的可组合性和可重用性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // small, reusable auxiliary functions const createSet = xs => new Set(xs); const filter = f => xs => xs.filter(apply(f)); const apply = f => x => f(x); // intersection const intersect = xs => ys => { const zs = createSet(ys); return filter(x => zs.has(x) ? true : false ) (xs); }; // mock data const xs = [1,2,2,3,4,5]; const ys = [0,1,2,3,3,3,6,7,8,9]; // run it console.log( intersect(xs) (ys) ); |
请注意,使用原生
查找性能。
避免重复
显然,保留了来自第一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // auxiliary functions const apply = f => x => f(x); const comp = f => g => x => f(g(x)); const afrom = apply(Array.from); const createSet = xs => new Set(xs); const filter = f => xs => xs.filter(apply(f)); // intersection const intersect = xs => ys => { const zs = createSet(ys); return filter(x => zs.has(x) ? true : false ) (xs); }; // de-duplication const dedupe = comp(afrom) (createSet); // mock data const xs = [1,2,2,3,4,5]; const ys = [0,1,2,3,3,3,6,7,8,9]; // unique result console.log( intersect(dedupe(xs)) (ys) ); |
计算任意数量
如果要计算任意数量的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // auxiliary functions const apply = f => x => f(x); const uncurry = f => (x, y) => f(x) (y); const createSet = xs => new Set(xs); const filter = f => xs => xs.filter(apply(f)); const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // intersection const intersect = xs => ys => { const zs = createSet(ys); return filter(x => zs.has(x) ? true : false ) (xs); }; // intersection of an arbitrarily number of Arrays const intersectn = (head, ...tail) => foldl(intersect) (head) (tail); // mock data const xs = [1,2,2,3,4,5]; const ys = [0,1,2,3,3,3,6,7,8,9]; const zs = [0,1,2,3,4,5,6]; // run console.log( intersectn(xs, ys, zs) ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 'use strict' // Example 1 function intersection(a1, a2) { return a1.filter(x => a2.indexOf(x) > -1) } // Example 2 (prototype function) Array.prototype.intersection = function(arr) { return this.filter(x => arr.indexOf(x) > -1) } const a1 = [1, 2, 3] const a2 = [2, 3, 4, 5] console.log(intersection(a1, a2)) console.log(a1.intersection(a2)) |
为简单起见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // Usage const intersection = allLists .reduce(intersect, allValues) .reduce(removeDuplicates, []); // Implementation const intersect = (intersection, list) => intersection.filter(item => list.some(x => x === item)); const removeDuplicates = (uniques, item) => uniques.includes(item) ? uniques : uniques.concat(item); // Example Data const somePeople = [bob, doug, jill]; const otherPeople = [sarah, bob, jill]; const morePeople = [jack, jill]; const allPeople = [...somePeople, ...otherPeople, ...morePeople]; const allGroups = [somePeople, otherPeople, morePeople]; // Example Usage const intersection = allGroups .reduce(intersect, allPeople) .reduce(removeDuplicates, []); intersection; // [jill] |
优点:
- 泥土简单
- 数据中心
- 适用于任意数量的列表
- 适用于任意长度的列表
- 适用于任意类型的值
- 适用于任意排序顺序
- 保留形状(任何阵列中首次出现的顺序)
- 尽可能早地退出
- 内存安全,没有篡改功能/数组原型
缺点:
- 内存使用率更高
- 更高的CPU使用率
- 需要了解减少
- 需要了解数据流
您不希望将此用于3D引擎或内核工作,但如果您在基于事件的应用程序中运行此问题时遇到问题,则您的设计会遇到更大的问题。
1 2 3 4 5 6 7 8 9 10 11 12 | function intersection (a, b) { var seen = a.reduce(function (h, k) { h[k] = true; return h; }, {}); return b.filter(function (k) { var exists = seen[k]; delete seen[k]; return exists; }); } |
我发现这种方法很容易推理。它在恒定的时间内执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 | function getIntersection(arr1, arr2){ var result = []; arr1.forEach(function(elem){ arr2.forEach(function(elem2){ if(elem === elem2){ result.push(elem); } }); }); return result; } getIntersection([1,2,3], [2,3,4,5]); // [ 2, 3 ] |
而是使用indexOf,您也可以使用Array.protype.includes。
1 2 3 4 5 6 7 | function intersection(arr1, arr2) { return arr1.filter((ele => { return arr2.includes(ele); })); } console.log(intersection([1,2,3], [2,3,4,5])); |
1 2 3 4 | var listA = [1,2,3,4,5,6,7]; var listB = [2,4,6,8]; var result = listA.filter(itemA => listB.some(itemB => itemB === itemA)); |
这是underscore.js实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | _.intersection = function(array) { if (array == null) return []; var result = []; var argsLength = arguments.length; for (var i = 0, length = array.length; i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; for (var j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); } return result; }; |
资料来源:http://underscorejs.org/docs/underscore.html#section-62
如果要使用已接受的答案,但需要支持Internet Explorer,则必须避免使用箭头功能速记注释。这是编辑过的单行程序,也可以在IE中使用:
1 2 3 | // accepted aswer: array1.filter(value => -1 !== array2.indexOf(value)); // IE-supported syntax: array1.filter(function(value) { return -1 !== array2.indexOf(value) }); |
如果你需要让它处理多个数组:
1 2 3 4 5 6 | const intersect = (a, b, ...rest) => { if (rest.length === 0) return [...new Set(a)].filter(x => new Set(b).has(x)); return intersect(a, intersect(b, ...rest)); }; console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) // [1,2] |
ES6风格简约的方式。
1 2 3 4 | const intersection = (a, b) => { const s = new Set(b); return a.filter(x => s.has(x)); }; |
例:
1 | intersection([1, 2, 3], [4, 3, 2]); // [2, 3] |
1 2 3 4 5 6 7 8 9 10 11 | var arrays = [ [1, 2, 3], [2, 3, 4, 5] ] function commonValue (...arr) { let res = arr[0].filter(function (x) { return arr.every((y) => y.includes(x)) }) return res; } commonValue(...arrays); |
您可以使用
1 2 3 4 5 | function intersection(a, b) { return a.filter(Set.prototype.has, new Set(b)); } console.log(intersection([1, 2, 3], [2, 3, 4, 5])); |
我编写了一个intesection函数,它甚至可以根据这些对象的特定属性检测对象数组的交集。
例如,
1 2 | if arr1 = [{id: 10}, {id: 20}] and arr2 = [{id: 20}, {id: 25}] |
我们想要基于
1 | [{id: 20}] |
因此,相同的功能(注意:ES6代码)是:
1 2 3 4 5 | const intersect = (arr1, arr2, accessors = [v => v, v => v]) => { const [fn1, fn2] = accessors; const set = new Set(arr2.map(v => fn2(v))); return arr1.filter(value => set.has(fn1(value))); }; |
你可以把这个函数称为:
1 | intersect(arr1, arr2, [elem => elem.id, elem => elem.id]) |
另请注意:此函数在考虑第一个数组是主数组时找到交集,因此交集结果将是主数组的交集结果。
1 2 3 | function intersectionOfArrays(arr1, arr2) { return arr1.filter((element) => arr2.indexOf(element) !== -1).filter((element, pos, self) => self.indexOf(element) == pos); } |
这是我正在使用的一个非常天真的实现。它是非破坏性的,也确保不复制entires。
1 2 3 4 5 6 7 8 9 10 11 12 13 | Array.prototype.contains = function(elem) { return(this.indexOf(elem) > -1); }; Array.prototype.intersect = function( array ) { // this is naive--could use some optimization var result = []; for ( var i = 0; i < this.length; i++ ) { if ( array.contains(this[i]) && !result.contains(this[i]) ) result.push( this[i] ); } return result; } |
使用一个数组创建一个Object并循环遍历第二个数组以检查该值是否作为键存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function intersection(arr1, arr2) { var myObj = {}; var myArr = []; for (var i = 0, len = arr1.length; i < len; i += 1) { if(myObj[arr1[i]]) { myObj[arr1[i]] += 1; } else { myObj[arr1[i]] = 1; } } for (var j = 0, len = arr2.length; j < len; j += 1) { if(myObj[arr2[j]] && myArr.indexOf(arr2[j]) === -1) { myArr.push(arr2[j]); } } return myArr; } |
如果第二个数组总是作为一个集处理,则不需要在函数内部为第二个数组声明一个中间变量。
以下解决方案返回两个数组中出现的唯一值数组:
1 2 3 4 5 6 | const intersection = (a, b) => { b = new Set(b); // recycling variable return [...new Set(a)].filter(e => b.has(e)); }; console.log(intersection([1, 2, 3, 1, 1], [1, 2, 4])); // Array [ 1, 2 ] |
coffeescript中N个数组的交集
1 2 3 4 5 6 7 8 | getIntersection: (arrays) -> if not arrays.length return [] a1 = arrays[0] for a2 in arrays.slice(1) a = (val for val in a1 when val in a2) a1 = a return a1.unique() |
如果您的数组已排序,则应在O(n)中运行,其中n为min(a.length,b.length)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function intersect_1d( a, b ){ var out=[], ai=0, bi=0, acurr, bcurr, last=Number.MIN_SAFE_INTEGER; while( ( acurr=a[ai] )!==undefined && ( bcurr=b[bi] )!==undefined ){ if( acurr < bcurr){ if( last===acurr ){ out.push( acurr ); } last=acurr; ai++; } else if( acurr > bcurr){ if( last===bcurr ){ out.push( bcurr ); } last=bcurr; bi++; } else { out.push( acurr ); last=acurr; ai++; bi++; } } return out; } |
以下代码也删除了重复项:
1 2 3 4 5 6 | function intersect(x, y) { if (y.length > x.length) temp = y, y = x, x= temp; return x.filter(function (e, i, c) { return c.indexOf(e) === i; }); } |
我扩展了tarulen的答案,可以使用任意数量的数组。它也应该使用非整数值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function intersect() { const last = arguments.length - 1; var seen={}; var result=[]; for (var i = 0; i < last; i++) { for (var j = 0; j < arguments[i].length; j++) { if (seen[arguments[i][j]]) { seen[arguments[i][j]] += 1; } else if (!i) { seen[arguments[i][j]] = 1; } } } for (var i = 0; i < arguments[last].length; i++) { if ( seen[arguments[last][i]] === last) result.push(arguments[last][i]); } return result; } |
希望这有助于所有版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | function diffArray(arr1, arr2) { var newArr = []; var large = arr1.length>=arr2.length?arr1:arr2; var small = JSON.stringify(large) == JSON.stringify(arr1)?arr2:arr1; for(var i=0;i<large.length;i++){ var copyExists = false; for(var j =0;j<small.length;j++){ if(large[i]==small[j]){ copyExists= true; break; } } if(!copyExists) { newArr.push(large[i]); } } for(var i=0;i<small.length;i++){ var copyExists = false; for(var j =0;j<large.length;j++){ if(large[j]==small[i]){ copyExists= true; break; } } if(!copyExists) { newArr.push(small[i]); } } return newArr; } |
在Anon的优秀答案的基础上,这个返回两个或更多阵列的交集。
1 2 3 4 5 6 7 8 9 10 11 | function arrayIntersect(arrayOfArrays) { var arrayCopy = arrayOfArrays.slice(), baseArray = arrayCopy.pop(); return baseArray.filter(function(item) { return arrayCopy.every(function(itemList) { return itemList.indexOf(item) !== -1; }); }); } |
不是效率,而是易于遵循,这里是集合的联合和交集的一个例子,它处理集合和集合的数组。
http://jsfiddle.net/zhulien/NF68T/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | // process array [element, element...], if allow abort ignore the result function processArray(arr_a, cb_a, blnAllowAbort_a) { var arrResult = []; var blnAborted = false; var intI = 0; while ((intI < arr_a.length) && (blnAborted === false)) { if (blnAllowAbort_a) { blnAborted = cb_a(arr_a[intI]); } else { arrResult[intI] = cb_a(arr_a[intI]); } intI++; } return arrResult; } // process array of operations [operation,arguments...] function processOperations(arrOperations_a) { var arrResult = []; var fnOperationE; for(var intI = 0, intR = 0; intI < arrOperations_a.length; intI+=2, intR++) { var fnOperation = arrOperations_a[intI+0]; var fnArgs = arrOperations_a[intI+1]; if (fnArgs === undefined) { arrResult[intR] = fnOperation(); } else { arrResult[intR] = fnOperation(fnArgs); } } return arrResult; } // return whether an element exists in an array function find(arr_a, varElement_a) { var blnResult = false; processArray(arr_a, function(varToMatch_a) { var blnAbort = false; if (varToMatch_a === varElement_a) { blnResult = true; blnAbort = true; } return blnAbort; }, true); return blnResult; } // return the union of all sets function union(arr_a) { var arrResult = []; var intI = 0; processArray(arr_a, function(arrSet_a) { processArray(arrSet_a, function(varElement_a) { // if the element doesn't exist in our result if (find(arrResult, varElement_a) === false) { // add it arrResult[intI] = varElement_a; intI++; } }); }); return arrResult; } // return the intersection of all sets function intersection(arr_a) { var arrResult = []; var intI = 0; // for each set processArray(arr_a, function(arrSet_a) { // every number is a candidate processArray(arrSet_a, function(varCandidate_a) { var blnCandidate = true; // for each set processArray(arr_a, function(arrSet_a) { // check that the candidate exists var blnFoundPart = find(arrSet_a, varCandidate_a); // if the candidate does not exist if (blnFoundPart === false) { // no longer a candidate blnCandidate = false; } }); if (blnCandidate) { // if the candidate doesn't exist in our result if (find(arrResult, varCandidate_a) === false) { // add it arrResult[intI] = varCandidate_a; intI++; } } }); }); return arrResult; } var strOutput = '' var arrSet1 = [1,2,3]; var arrSet2 = [2,5,6]; var arrSet3 = [7,8,9,2]; // return the union of the sets strOutput = union([arrSet1, arrSet2, arrSet3]); alert(strOutput); // return the intersection of 3 sets strOutput = intersection([arrSet1, arrSet2, arrSet3]); alert(strOutput); // of 3 sets of sets, which set is the intersecting set strOutput = processOperations([intersection,[[arrSet1, arrSet2], [arrSet2], [arrSet2, arrSet3]]]); alert(strOutput); |
IE中的Array不支持"filter"和"indexOf"。这个怎么样:
1 2 3 4 5 6 7 8 9 | var array1 = [1, 2, 3]; var array2 = [2, 3, 4, 5]; var intersection = []; for (i in array1) { for (j in array2) { if (array1[i] == array2[j]) intersection.push(array1[i]); } } |