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

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


并得到

[2, 3]


评论

您想要简单还是快速?

优先级很简单,但是不能太死脑筋,以至于会成为表演猪:)

我为这里的所有方法(包括_underscore相交函数)制作了一个JsFiddle Banchmark测试页。 (越高越好)!在此处输入图像描述直到现在intersect_safe给出了最佳结果。你和强调最坏的情况。

在简单的js循环中添加中断可将操作/秒提高到〜10M

真好!但是,如果它们不是数字类型呢?如果它们是需要自定义检查的自定义对象怎么办?

#1 楼

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

NB! .includes.indexOf都使用===在内部比较数组中的元素,因此,如果数组包含对象,则它将仅比较对象引用(不比较其内容)。如果要指定自己的比较逻辑,请改用.some

评论


此处的最佳答案,既简便又适用于非数字

– Muhd
13年8月10日,0:43

对于那些好奇的人来说,使该工作正常的IE最低版本为:9

– bashaus
15年8月14日在9:23

交集([1,2,1,1,3],[1])返回[1,1,1]。它不应该只返回[1]吗?

– edjroot
16年6月10日在10:35

接得好。您将需要第二个过滤器。请参阅此处的示例:stackoverflow.com/a/16227294/390519(该答案末尾的<-)

–loneboat
16年7月5日在23:56

这不是很低效吗,是O(n ^ 2)。

–paul23
6月1日17:26

#2 楼

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

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


评论


intersect_safe中存在许多错误:length是Arrays中的属性,而不是方法。在result.push(a [i]);中有一个不喜欢的变量i。最后,这在一般情况下根本行不通:根据>运算符,两个对象都不大于另一个的对象不一定相等。例如,intersect_safe([[{}],[{}])将给出(一旦修复了前面提到的错误)带有一个元素的数组,这显然是错误的。

– Tim Down
09年12月11日11:00

@Tim Down:更正了您指出的语法错误。认为不等于或小于等于的任何东西是正确还是不正确取决于要求。我在原始问题中没有注意到任何内容,即内容应包含数组。现在,您应该正确地说应该处理意外的输入,但是如果规范已经碰巧表明输入必须是数字数组(如我假设的那样),那么代码就可以了。

–atk
09年12月11日14:46

@atk:我同意你的观点,因为问题中的示例使用仅包含数字的数组。

– Tim Down
09年12月11日在16:08

您也可以使用.slice(0)在intersect_safe中创建数组的克隆,而不是跟踪索引。

– johnluetke
2011-09-10 23:42



.shift需要移动数组中的每个元素(本身为O(n)),因此有关第一个版本的复杂性的注释是错误的

– Esailija
13年7月23日在8:46



#3 楼

如果您的环境支持ECMAScript 6 Set,则一种简单且据认为有效的方法(请参阅规范链接): ):

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的值为new Set([1, 2, 3, 3]).size

评论


[... setA]的语法是什么?一些特殊的javascript操作?

– jxramos
17年7月21日在20:01

@jxramos是Spread语法,在这种情况下,它仅用于根据集合中的元素创建数组。在这种情况下,“ Array.from(setA)”也可以使用,但是由于该问题要求“最简单”,因此我尝试使其在该行中更清晰易读。在这个问题上,我认为代码可能更简单,因此我将更新代码段。

–nbarbosa
17年7月21日在20:41



@nbarbosa我很好奇:为什么您要“克隆”数组? #filter不会破坏原始数组,对吗?它创建一个新的数组?

– bplittle
17年11月23日在2:22

@bplittle我只是从集合中创建了一个数组以删除重复项,否则,直接使用该数组将导致返回重复项。例如,如果我直接使用数组,则intersect([1,2,2,4],[2,3])将产生[2,2]。

–nbarbosa
17年10月10日在4:05

在第一个示例(删除重复项)中,无需同时从a和交集创建集合。一个或另一个就足够了。

–Ry-♦
6月15日下午16:53

#4 楼

使用Underscore.js或lodash.js

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


评论


Op要求“无库”。

– LinuxDisciple
16年11月23日在20:50

@LinuxDisciple我错过的错误。感谢您的注释

–西拉姆
16年11月25日在5:00

无论如何,这是此搜索的Google顶部列表,因此有一个图书馆答案很有用。谢谢。

–webnoob
17年1月18日在9:25

我也很高兴这已经发布。第一次,我感到需要使用underscore.js。通常,JavaScript map和reduce管道可以很好地完成工作,但这次却不能。

– Sridhar Sarnobat
17年11月8日在2:37

我<3下划线,我<3杰里米·阿什肯纳斯(其创建者),但即使如此,我还是强烈建议您改用Lodash。它基本上是Underscore的高级版本(最初是fork),其唯一缺点是超级优化了(因此几乎不可读)源代码。 Underscore的人们甚至考虑完全摆脱Underscore(只是告诉人们使用Lodash),但是关心源代码可读性的人们则主张保留它(实际上我是站在那一边,但是自那以后我就转换为Lodash)。 @see github.com/jashkenas/underscore/issues/2182

–machineghost
18年1月7日在18:59



#5 楼




 // 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个元素):

 filter + has (this)               523 (this answer)
for + has                         482
for-loop + in                     279
filter + in                       242
for-loops                          24
filter + includes                  14
filter + indexOf                   10
 



小数组上的每秒操作数(100个元素):

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




评论


intersect([1,2,2,3],[2,3,4,5])返回[2,2,3]。

– SeregPie
17年5月12日在9:25



@SeregPie确实是。根据评论“返回数组a的元素也位于b中”

–le_m
17年5月12日在10:32

质量答案,但是使用集会从根本上改变结果,因为op的问题仅询问数组交集,而未提及/规定如何处理重复项。害羞的是,当重复项存在时,此答案可能会产生意外结果。

–破解
18-10-4在15:16



喜欢它,但是您已经使用“ filter + includes”添加了一个不必要的功能。尝试使用a.filter(b.includes)。它应该运行得更快(与您的功能升级相同)。

– SEoF
19年2月6日在15:06

#6 楼

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




评论


该代码看起来不错,但我并不完全理解。可以解释一下吗?

–通常
18年4月10日在17:35

@novembersky它以[[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8],[4,5] ,6,7],[4,6]],然后应用.reduce()。首先[0,1,2,3,4,5,6,7,8,9] .filter(e => [0,2,4,6,8] .includes(e)操作并执行结果变成新的p,而c在下一个回合中变为[4,5,6,7]并继续下去,直到没有c为止。

– Redu
18年4月11日在2:37

如果要处理大型数据集,这是一个昂贵的解决方案。

–破解
18-10-4在15:13

不要不必要地修改原型。

–fregante
19年1月4日在12:16

#7 楼

如何仅使用关联数组?

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


评论


如果您的数组仅包含字符串或数字,并且页面中的所有脚本均未与Object.prototype混淆,则这仅是一个机会。

– Tim Down
09年12月11日上午10:49

OP的示例使用数字,如果脚本与Object.prototype混淆,则应重写或删除脚本。

–史蒂芬·休格(Steven Huwig)
09年12月11日15:25

您不需要(d1)和(d2)。创建(d2),然后遍历(a)而不是遍历(d1)。

– StanleyH
2011-2-23在10:54

应该是d [b [i]] = true;而不是d [b [j]] = true; (我不是j)。但是编辑需要6个字符。

–茨城
2012年7月30日在1:59

@Izhaki谢谢,固定。 (添加了//注释,以避开最低编辑要求。)

–史蒂芬·休格(Steven Huwig)
2012年7月30日15:01

#8 楼


将其排序
从索引0一项开始进行检查,然后从索引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:仅适用于数字和普通字符串的算法,任意对象数组的交集可能不起作用。

评论


排序不一定对任意对象的数组有用

– Tim Down
09年12月11日上午10:47

如果数组未排序,则在相交1000长度数组x 1000长度数组时需要循环1,000,000次

–您
09年12月11日14:49

我想您错过了我的观点,那就是JavaScript中的任意对象没有自然的排序顺序,这意味着对任意对象的数组进行排序不会导致相等的对象被分组。无效的有效算法不好。

– Tim Down
09年12月14日在2:08

抱歉,我错过了“任意物品”,是的,您是对的。这些对象无法对其进行排序,并且该算法可能无法对其进行处理。

–您
09年12月14日下午2:10

#9 楼

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

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的速度大约快三倍。

评论


就像其他注释一样-仅适用于数字,不适用于字符串。

–茨城
2012年7月30日,0:15

请注意,如果将a [aLast]> b [bLast]替换为a [aLast] .localeCompare(b [bLast])> 0(并且与下面的else相同),那么这将适用于字符串。

– andrew
13年7月23日在5:10

速度差取决于数组的大小,因为.pop为O(1)而.shift()为O(n)

– Esailija
13年7月23日在8:53

#10 楼

使用jQuery:

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


评论


这也可以写成c = $(b).filter(a);,但我不建议依赖jQuery进行这种数组操作,因为文档只提到它适用于元素。

–Stryner
15年9月23日在18:16

这不能回答op的问题:“什么是最简单的,无库代码...”

–破解
18-10-4在15:14

#11 楼

对于仅包含字符串或数字的数组,您可以按照其他一些答案进行排序。对于任意对象数组的一般情况,我认为您不能避免这样做。以下将为您提供作为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 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"]; 


评论


这仅在对象标识是唯一的相等形式的情况下有效。

–史蒂芬·休格(Steven Huwig)
09年12月15日在14:04

是的,但是我认为这对大多数人来说似乎很自然。插入替代功能以执行不同的相等性测试也很简单。

– Tim Down
09年12月15日在19:31

我认为您在示例中无意中创建了一个全局变量firstArr。

–Jason Jackson
17年1月31日23:00

@JasonJackson:对,谢谢。显然改变了我关于是否要调用变量firstArr或firstArray的想法,并且没有更新所有引用。固定。

– Tim Down
17年2月1日在11:43

#12 楼

对此处的最小调整进行细微调整(filter / indexOf解决方案),即使用JavaScript对象在数组之一中创建值的索引,会将其从O(N * M)减少为“大概”线性时间。 source1 source2

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()更好,但看起来很不错。它在非常简单的方面,同时提供了良好的性能,并且不需要预先排序的输入。

#13 楼

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

// 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]++;


。 ..and:

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


评论


稍加修改,这是最好的答案。在var v =行之后添加if(typeof v =='function')继续;它将跳过向结果中添加函数的操作。谢谢!

– Zsolti
16年8月31日在7:58



谢谢@Zsolti。我没有添加您的建议,因为具有函数(以及我们要处理它的方式)超出了原始问题的范围。但是请看我的编辑:如果输入数组中可以包含重复项,则原始实现存在问题。我在编辑中修复了它。 ...另一方面,如果您确定不会重复,那么原始的实现会便宜一些。

– bitifet
16-10-6在6:01

...关于函数,它们也可以相交:如果我们像@Zsolti所说的那样检测它们(使用if(typeof v =='function'),那么我们可以使用其字符串化(v.toString())作为索引,但是,我们需要做一些事情以保持其完好无损,最简单的方法就是将原始函数指定为值,而不是简单的布尔真实值,但是在这种情况下,最新的索引也应更改为检测这种情况并恢复正确的值(该功能)。

– bitifet
17年8月16日在7:42

30个具有100个元素的阵列的速度有多快。CPU使用率如何?

– adonsnous
19年1月6日在10:58

#14 楼

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

对于正整数:使用将值映射到“可见/不可见”布尔值的数组。

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


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

评论


顺便说一句,可以很容易地扩展它以与任意数量的数组相交:用整数替换布尔值,并在每次看到布尔值时递增:您可以很容易地读取上一轮的交集。

– Tarulen
2015年10月5日,12:22

有趣的解决方案,我喜欢。大多数其他解决方案是O(n ^ 2),但这是O(n)。我在jsfiddle.net/321juyLu/2处将整数代码添加到ericP的性能中。排到第三,我很喜欢:)

–rmcsharry
16-09-18在8:16



#15 楼

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


#16 楼

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

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


#17 楼

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




评论


令人印象深刻的功能:必须经过两次验证才能确定不是Haskell。唯一的nitpick是:(expr?true:false)是多余的。如果不需要实际的布尔值,则仅使用expr,如实/虚假。

– jose_castro_arnaud
17年11月28日在17:48

毫无意义的复杂,并且相交效率低下。这是编写可重用函数的错误方法。

–Ry-♦
6月15日在16:46



与“毫无意义的复杂”相比,这个答案值得更多的考虑。如果有人受到它的影响,那么等于“错误”的评论只会引起混乱。

– Corey
12月7日7:37

#18 楼

为简单起见:

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




dirt simple
以数据为中心列表数量
用于任意长度的列表
用于任意类型的值
用于任意排序顺序
保持形状(在任何数组中首次出现的顺序)
在可能的情况下尽早退出
内存安全,不会篡改Function / Array原型

缺点:


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

您不想将其用于3D引擎或内核工作,但是如果您在获取此信息时遇到问题要在基于事件的应用程序中运行,您的设计会遇到更大的问题。

评论


.some(x => x === item)是编写.includes(item)的复杂方法,而.reduce(removeDuplicates,[])是编写flatten + unique的复杂方法。这也可能比您想象的要慢(与基于事件的应用程序很相关)。

–Ry-♦
6月15日16:23



#19 楼

.reduce建立地图,.filter找到交点。 delete中的.filter使我们可以将第二个数组视为唯一数组。

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


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

#20 楼

我编写了一个积分函数,该函数甚至可以根据那些对象的特定属性来检测对象数组的交集。例如,

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


还请注意:该函数在考虑交叉点的情况下第一个数组是主数组,因此相交结果将是主数组的交集结果。

#21 楼

如果需要让它处理相交的多个数组:



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




评论


但是,此解决方案对于包含100个元素的30个数组有多快?

– adonsnous
19年1月6日在10:50

它只使用Javascript方法的本机,因此将在其中运行代码的VM尽可能自由地对其进行优化。我非常肯定没有相对更快的解决方案,因为相对于此评论的年龄,您正在以现代版本的V8运行此解决方案。

–贝尔福德
19年5月28日在20:01

@Belfordz:不,由于所有阵列复制和不必要的集重建,这非常慢。不保留集合而使用新Set(b).has(x)甚至比b.includes(x)还要慢。

–Ry-♦
6月15日下午16:28

#22 楼

这是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

评论


如果undesrcore可用,这不是一个不好的参考

– Dimitrios Mistriotis
2015年11月23日下午13:00

#23 楼

这可能是最简单的列表,
除了list1.filter(n => list2.includes(n))




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




评论


这具有O(nm)的时间复杂性...这可以在O(n + m)中解决

– alchuang
3月11日7:11

#24 楼

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

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


#25 楼

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

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


#26 楼

我扩展了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;
}


#27 楼

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 ]


#28 楼

如果您的数组已排序,则应在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;
}


#29 楼




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




#30 楼

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