JavaScript中数组交集的最简单代码

在javascript中实现数组交集的最简单、无库的代码是什么?我想写

intersection([1,2,3], [2,3,4,5])

并获得

[2, 3]
708607 次浏览

使用Array.prototype.filterArray.prototype.includes的组合:

const filteredArray = array1.filter(value => array2.includes(value));

对于较旧的浏览器,使用Array.prototype.indexOf且没有箭头函数:

var filteredArray = array1.filter(function(n) {return array2.indexOf(n) !== -1;});

注意!.includes.indexOf都使用===在内部比较数组中的元素,因此如果数组包含对象,它将只比较对象引用(而不是它们的内容)。如果您想指定自己的比较逻辑,请改用Array.prototype.some

  1. 整理一下
  2. 从索引0逐个检查,从中创建新数组。

像这样的东西,虽然没有测试好。

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:该算法仅适用于数字和普通字符串,任意对象数组的交集可能不起作用。

破坏性似乎最简单,特别是如果我们可以假设输入是排序的:

/* 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;}

非破坏性必须更复杂,因为我们必须跟踪索引:

/* 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;}

如何只使用关联数组?

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

编辑:

// new versionfunction 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;}

IE的Array不支持“filter”和“indexOf”。这样:

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

对于仅包含字符串或数字的数组,您可以根据其他一些答案进行排序。对于任意对象数组的一般情况,我认为您无法避免这样做。以下将为您提供作为参数提供给arrayIntersection的任意数量数组的交集:

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 valuesfirstArray = arrays.pop();if (firstArray) {j = firstArray.length;arrayCount = arrays.length;while (j--) {val = firstArray[j];missing = false;
// Check val is present in each remaining arrayi = 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"];

这是我正在使用的一个非常简单的实现。它是非破坏性的,并且还确保不重复整体。

Array.prototype.contains = function(elem) {return(this.indexOf(elem) > -1);};
Array.prototype.intersect = function( array ) {// this is naive--could use some optimizationvar result = [];for ( var i = 0; i < this.length; i++ ) {if ( array.contains(this[i]) && !result.contains(this[i]) )result.push( this[i] );}return result;}

@atk对原语排序数组的实现的性能可以通过使用. pop而不是. shift来提高。

function intersect(array1, array2) {var result = [];// Don't destroy the original arraysvar 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创建了一个基准。使用. pop大约快三倍。

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

coffeescript中N个数组的交集

getIntersection: (arrays) ->if not arrays.lengthreturn []a1 = arrays[0]for a2 in arrays.slice(1)a = (val for val in a1 when val in a2)a1 = areturn a1.unique()

我将用最适合我的方式做出贡献:

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

使用jQuery

var a = [1,2,3];var b = [2,3,4,5];var c = $(b).not($(b).not(a));alert(c);

不是关于效率,而是容易理解,这里有一个集合的联合和交叉的例子,它处理集合的数组和集合的集合。

// process array [element, element...], if allow abort ignore the resultfunction 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 arrayfunction 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 setsfunction 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 resultif (find(arrResult, varElement_a) === false){// add itarrResult[intI] = varElement_a;intI++;}});});
return arrResult;}
// return the intersection of all setsfunction intersection(arr_a){var arrResult = [];var intI = 0;
// for each setprocessArray(arr_a, function(arrSet_a){// every number is a candidateprocessArray(arrSet_a, function(varCandidate_a){var blnCandidate = true;
// for each setprocessArray(arr_a, function(arrSet_a){// check that the candidate existsvar blnFoundPart = find(arrSet_a, varCandidate_a);
// if the candidate does not existif (blnFoundPart === false){// no longer a candidateblnCandidate = false;}});
if (blnCandidate){// if the candidate doesn't exist in our resultif (find(arrResult, varCandidate_a) === false){// add itarrResult[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 setsstrOutput = union([arrSet1, arrSet2, arrSet3]);alert(strOutput);
// return the intersection of 3 setsstrOutput = intersection([arrSet1, arrSet2, arrSet3]);alert(strOutput);
// of 3 sets of sets, which set is the intersecting setstrOutput = processOperations([intersection,[[arrSet1, arrSet2], [arrSet2], [arrSet2, arrSet3]]]);alert(strOutput);

以下是underscore.js实现:

_.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

另一种索引方法能够一次处理任意数量的数组:

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

它仅适用于可以作为字符串评估的值,您应该将它们作为数组传递,例如:

intersect ([arr1, arr2, arr3...]);

…但它透明地接受对象作为参数或任何要相交的元素(总是返回公共值的数组)。例子:

intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]

编辑:我只是注意到这在某种程度上有点错误。

也就是说:我对它进行编码时认为输入数组本身不能包含重复(因为提供的示例不包含重复)。

但是如果输入数组碰巧包含重复,那将产生错误的结果。示例(使用下面的实现):

intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);// Expected: [ '1' ]// Actual: [ '1', '3' ]

幸运的是,这很容易通过简单地添加二级索引来解决。即:

更改:

        if (index[v] === undefined) index[v] = 0;index[v]++;

通过:

        if (index[v] === undefined) index[v] = {};index[v][i] = true; // Mark as present in i input.

.和:

         if (index[i] == arrLength) retv.push(i);

通过:

         if (Object.keys(index[i]).length == arrLength) retv.push(i);

完整示例:

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

对您的数据有一些限制,您可以在线性时间内完成!

对于正整数:使用一个数组将值映射到“看到/没有看到”布尔值。

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的元素中查找此键。完成后清理。

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

当然,您需要确保密钥之前没有出现,否则您将破坏您的数据。

对这里最小的一个(filter/indexOf解决方案)进行微小的调整,即使用JavaScript对象创建一个数组中的值的索引,将把它从O(N*M)减少到“可能”的线性时间。

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()慢一个常数因子),但似乎是一个很好的平衡。它在非常简单的一面,同时提供了良好的性能,并且不需要预先排序的输入。

如果您的环境支持ECMAScript 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);}

更短,但可读性较差(也没有创建额外的交集Set):

function intersect(a, b) {var setB = new Set(b);return [...new Set(a)].filter(x => setB.has(x));}

请注意,使用集合时,您只会获得不同的值,因此new Set([1, 2, 3, 3]).size的计算结果为3

我在ES6术语中的贡献。一般来说,它找到一个数组的交集,其中包含作为参数提供的不确定数量的数组。

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("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");

我将tarulen的答案扩展到可以处理任意数量的数组。它也应该适用于非整数值。

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

使用Underscore.jslodash.js

_.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]

ES2015的功能方法

函数式方法必须考虑只使用没有副作用的纯函数,每个函数只涉及一个作业。

这些限制增强了所涉及函数的可组合性和可重用性。

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

请注意,使用了原生Set类型,这具有优势查找性能。

避免重复

第一个Array中明显重复发生的项目被保留,而第二个Array被去重复。这可能是也可能不是所需的行为。如果您需要唯一的结果,只需将dedupe应用于第一个参数:

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

计算任意数量的Array的交集

如果您想计算任意数量的Array的交集,只需将intersectfoldl组合。这是一个方便的函数:

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

基于Anon的优秀答案,这个返回两个或多个数组的交集。

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

希望对所有版本都有帮助。

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

为简单起见:

// Usageconst intersection = allLists.reduce(intersect, allValues).reduce(removeDuplicates, []);

// Implementationconst intersect = (intersection, list) =>intersection.filter(item =>list.some(x => x === item));
const removeDuplicates = (uniques, item) =>uniques.includes(item) ? uniques : uniques.concat(item);

// Example Dataconst somePeople = [bob, doug, jill];const otherPeople = [sarah, bob, jill];const morePeople = [jack, jill];
const allPeople = [...somePeople, ...otherPeople, ...morePeople];const allGroups = [somePeople, otherPeople, morePeople];
// Example Usageconst intersection = allGroups.reduce(intersect, allPeople).reduce(removeDuplicates, []);
intersection; // [jill]

好处:

  • 污垢简单
  • 以数据为中心
  • 适用于任意数量的列表
  • 适用于任意长度的列表
  • 适用于任意类型的值
  • 适用于任意排序顺序
  • 保留形状(任何数组中首次出现的顺序)
  • 尽可能早地退出
  • 内存安全,无需篡改函数/数组原型

缺点:

  • 更高的内存使用率
  • 更高的CPU使用率
  • 需要了解减少
  • 需要了解数据流

您不希望将其用于3D引擎或内核工作,但如果您在基于事件的应用程序中运行它时遇到问题,那么您的设计就会遇到更大的问题。

.reduce用于构建映射,.filter用于查找交集。.filter中的delete允许我们将第二个数组视为唯一集。

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

我发现这种方法很容易推理。它在恒定的时间内执行。

// 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以进行性能比较。

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的结果:

  • 大数组(10,000个元素)上的Ops/sec:

    filter + has (this)               523 (this answer)for + has                         482for-loop + in                     279filter + in                       242for-loops                          24filter + includes                  14filter + indexOf                   10
  • Ops/sec on small arrays (100 elements):

    for-loop + in                 384,426filter + in                   192,066for-loops                     159,137filter + includes             104,068filter + indexOf               71,598filter + has (this)            43,531 (this answer)filter + has (arrow function)  35,588

如果您需要让它处理交叉多个数组:

const intersect = (a1, a2, ...rest) => {const a12 = a1.filter(value => a2.includes(value))if (rest.length === 0) { return a12; }return intersect(a12, ...rest);};
console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1])) 

如果您的数组已排序,则应在O(n)中运行,其中n为min(a.length,b.length)

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

使用一个数组创建一个对象,并循环遍历第二个数组以检查该值是否作为键存在。

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

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

function intersectionOfArrays(arr1, arr2) {return arr1.filter((element) => arr2.indexOf(element) !== -1).filter((element, pos, self) => self.indexOf(element) == pos);}

我写了一个intesection函数,它甚至可以根据对象的特定属性检测对象数组的交集。

例如,

if arr1 = [{id: 10}, {id: 20}]and arr2 =  [{id: 20}, {id: 25}]

我们想要基于id属性的交集,那么输出应该是:

[{id: 20}]

因此,相同的函数(注:ES6代码)是:

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

您可以将该函数调用为:

intersect(arr1, arr2, [elem => elem.id, elem => elem.id])

另请注意:此函数查找交集,考虑到第一个数组是主数组,因此交集结果将是主数组的结果。

这是一种现代而简单的ES6方式,也非常灵活。它允许您指定多个数组作为要比较主题数组的数组,并且可以在包含和排他模式下工作。

// =======================================// The function// =======================================
function assoc(subjectArray, otherArrays, { mustBeInAll = true } = {}) {return subjectArray.filter((subjectItem) => {if (mustBeInAll) {return otherArrays.every((otherArray) =>otherArray.includes(subjectItem));} else {return otherArrays.some((otherArray) => otherArray.includes(subjectItem));}});}
// =======================================// The usage// =======================================
const cheeseList = ["stilton", "edam", "cheddar", "brie"];const foodListCollection = [["cakes", "ham", "stilton"],["juice", "wine", "brie", "bread", "stilton"]];
// Output will be: ['stilton', 'brie']const inclusive = assoc(cheeseList, foodListCollection, { mustBeInAll: false }),
// Output will be: ['stilton']const exclusive = assoc(cheeseList, foodListCollection, { mustBeInAll: true })

直播示例:https://codesandbox.io/s/zealous-butterfly-h7dgf?fontsize=14&;隐藏=1&主题=黑暗

此函数避免了N^2问题,利用字典的强大功能。仅在每个数组中循环一次,第三次且更短的循环返回最终结果。支持数字、字符串和对象.

function array_intersect(array1, array2){var mergedElems = {},result = [];
// Returns a unique reference string for the type and value of the elementfunction generateStrKey(elem) {var typeOfElem = typeof elem;if (typeOfElem === 'object') {typeOfElem += Object.prototype.toString.call(elem);}return [typeOfElem, elem.toString(), JSON.stringify(elem)].join('__');}
array1.forEach(function(elem) {var key = generateStrKey(elem);if (!(key in mergedElems)) {mergedElems[key] = {elem: elem, inArray2: false};}});
array2.forEach(function(elem) {var key = generateStrKey(elem);if (key in mergedElems) {mergedElems[key].inArray2 = true;}});
Object.values(mergedElems).forEach(function(elem) {if (elem.inArray2) {result.push(elem.elem);}});
return result;}

如果有无法解决的特殊情况,只需修改generateStrKey函数,肯定可以解决。这个函数的诀窍是它根据类型和值唯一地表示每个不同的数据。


这个变体有一些性能改进。如果任何数组为空,请避免循环。它还首先遍历较短的数组,因此如果它在第二个数组中找到第一个数组的所有值,则退出循环。

function array_intersect(array1, array2){var mergedElems = {},result = [],firstArray, secondArray,firstN = 0,secondN = 0;
function generateStrKey(elem) {var typeOfElem = typeof elem;if (typeOfElem === 'object') {typeOfElem += Object.prototype.toString.call(elem);}return [typeOfElem, elem.toString(), JSON.stringify(elem)].join('__');}
// Executes the loops only if both arrays have valuesif (array1.length && array2.length){// Begins with the shortest array to optimize the algorithmif (array1.length < array2.length) {firstArray = array1;secondArray = array2;} else {firstArray = array2;secondArray = array1;}
firstArray.forEach(function(elem) {var key = generateStrKey(elem);if (!(key in mergedElems)) {mergedElems[key] = {elem: elem, inArray2: false};// Increases the counter of unique values in the first arrayfirstN++;}});
secondArray.some(function(elem) {var key = generateStrKey(elem);if (key in mergedElems) {if (!mergedElems[key].inArray2) {mergedElems[key].inArray2 = true;// Increases the counter of matchessecondN++;// If all elements of first array have coincidence, then exits the loopreturn (secondN === firstN);}}});
Object.values(mergedElems).forEach(function(elem) {if (elem.inArray2) {result.push(elem.elem);}});}
return result;}

我认为在内部使用对象可以帮助计算,并且也可以提高性能。

//方法维护每个元素的计数,也适用于负元素

function intersect(a,b){    
const A = {};a.forEach((v)=>{A[v] ? ++A[v] : A[v] = 1});const B = {};b.forEach((v)=>{B[v] ? ++B[v] : B[v] = 1});const C = {};Object.entries(A).map((x)=>C[x[0]] = Math.min(x[1],B[x[0]]))return Object.entries(C).map((x)=>Array(x[1]).fill(Number(x[0]))).flat();}const x = [1,1,-1,-1,0,0,2,2];const y = [2,0,1,1,1,1,0,-1,-1,-1];const result = intersect(x,y);console.log(result);  // (7) [0, 0, 1, 1, 2, -1, -1]

我使用地图甚至对象可以使用。

//find intersection of 2 arrsconst intersections = (arr1,arr2) => {let arrf = arr1.concat(arr2)let map = new Map();let union = [];for(let i=0; i<arrf.length; i++){if(map.get(arrf[i])){map.set(arrf[i],false);}else{map.set(arrf[i],true);}}map.forEach((v,k)=>{if(!v){union.push(k);}})return union;}

这是一个提议的标准:使用当前阶段2提案https://github.com/tc39/proposal-set-methods,您可以使用

mySet.intersection(mySet2);

在那之前,你可以使用Immutable.jsSet,这激发了这个提议

Immutable.Set(mySet).intersect(mySet2)

最简单,最快的O(n)和最短的方式:

function intersection (a, b) {const setA = new Set(a);return b.filter(value => setA.has(value));}
console.log(intersection([1,2,3], [2,3,4,5]))

@nbarbosa的答案几乎相同,但他将两个数组都转换为Set,然后返回到array。不需要任何额外的转换。

这是一个使用可选比较函数处理多个数组的简单实现:

function intersection(arrays, compareFn = (val1, val2) => (val1 === val2)) {if (arrays.length < 2) return arrays[0] ?? []const array1 = arrays[0]const array2 = intersection(arrays.slice(1), compareFn)return array1.filter(val1 => array2.some(val2 => compareFn(val1, val2)))}
console.log(intersection([[1, 2, 3], [2, 3, 4, 5]]))console.log(intersection([[{ id: 1 }, { id: 2 }], [{ id: 1 }, { id: 3 }]],(val1, val2) => val1.id === val2.id))