代码之家  ›  专栏  ›  技术社区  ›  Peter

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

  •  447
  • Peter  · 技术社区  · 15 年前

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

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

    得到

    [2, 3]
    
    44 回复  |  直到 5 年前
        1
  •  823
  •   Antoine Errorpro    5 年前

    结合使用 Array.prototype.filter Array.prototype.indexOf :

    array1.filter(value => -1 !== array2.indexOf(value))
    

    或作为 vrugtehagel 在评论中建议,您可以使用最近的 Array.prototype.includes 对于更简单的代码:

    array1.filter(value => array2.includes(value))
    
        2
  •  154
  •   Andreas Louv    8 年前

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

    /* 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;
    }
    
        3
  •  45
  •   nbarbosa    6 年前

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

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

    避新 集合 b 每一次:

    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 .

        4
  •  26
  •   Stalinko    6 年前

    使用 不足之处 J.S.

    _.intersection( [0,345,324] , [1,0,324] )  // gives [0,324]
    
        5
  •  12
  •   Redu    8 年前

    我在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>");
        6
  •  11
  •   Steven Huwig    12 年前

    只使用关联数组怎么样?

    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;
    }
    
        7
  •  8
  •   xn.    12 年前

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

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

    我使用jsper创建了一个基准: http://bit.ly/P9FrZK . 用起来快三倍。砰。

        8
  •  8
  •   le_m    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 用于性能比较。

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

    火狐53的结果:

    • 大型阵列上的每秒操作数(10000个元素):

      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
      
        9
  •  8
  •   frnhr    6 年前

    使用 JQuery 以下内容:

    var a = [1,2,3];
    var b = [2,3,4,5];
    var c = $(b).not($(b).not(a));
    alert(c);
    
        10
  •  7
  •   YOU    15 年前
    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:该算法只适用于数字和普通字符串,任意对象数组的交集可能不起作用。

        11
  •  7
  •   Tim Down    8 年前

    对于只包含字符串或数字的数组,可以按照其他一些答案进行排序。对于任意对象数组的一般情况,我认为您不能避免这么做。下面将给出作为参数提供的任意数量数组的交集 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"]; 
    
        12
  •  7
  •   SeregPie    7 年前

    使用ES2015和SETS非常短。接受类似数组的值(如字符串)并删除重复项。

    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(''));
        13
  •  5
  •   Community PPrice    7 年前

    对这里最小的一个 filter/indexOf solution ,也就是说,使用一个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() 但似乎是一个很好的平衡。这是关于 非常 简单的一面,同时提供良好的性能,而且不需要预先排序的输入。

        14
  •  5
  •   bitifet    8 年前

    另一种索引方法可以同时处理任意数量的数组:

    // 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' ]
    
        15
  •  4
  •   Gabe    12 年前
    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
  •  4
  •   tarulen    9 年前

    由于对数据有一些限制,您可以在 线性的 时间!

    为了 正整数 :使用数组将值映射到“已看到/未看到”布尔值。

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

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

        17
  •  3
  •   Johan    11 年前

    我将为我的工作做出贡献:

    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;
    };
    }
    
        18
  •  3
  •   Martin Roberto Mondragon Sotel    10 年前

    IE 9.0、Chrome、Firefox、Opera的“indexof”

        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]
    
        19
  •  3
  •   Chris Lwin    7 年前

    这可能是最简单的一个, 此外 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"]
        20
  •  2
  •   Vlad Bezden    8 年前

    '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))
        21
  •  2
  •   user6445533    8 年前

    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 保存,而第二个 数组 重复数据消除。这可能是或可能不是所需的行为。如果您需要一个独特的结果,只需应用 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) );

    计算任意数的交集 数组 S

    如果要计算任意数 数组 只是组成 intersect 具有 foldl . 这是一个便利功能:

    // 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) );
        22
  •  2
  •   LetterEh    8 年前

    为简单起见:

    // 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使用率
    • 需要了解reduce
    • 需要了解数据流

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

        23
  •  2
  •   Belden    7 年前

    .reduce 绘制地图,以及 .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;
      });
    }
    

    我觉得这种方法很容易理解。它在恒定时间内执行。

        24
  •  1
  •   Dimitrios Mistriotis    9 年前

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

        25
  •  1
  •   jcmordan    8 年前
    var listA = [1,2,3,4,5,6,7];
    var listB = [2,4,6,8];
    
    var result = listA.filter(itemA=> {
        return listB.some(itemB => itemB === itemA);
    });
    
        26
  •  1
  •   Bekzat    7 年前
    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 ]
    
        27
  •  1
  •   Belfordz    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]
        28
  •  1
  •   arizafar    6 年前

    相反,使用indexof也可以使用 Array.protype.includes .

    function intersection(arr1, arr2) {
      return arr1.filter((ele => {
        return arr2.includes(ele);
      }));
    }
    
    console.log(intersection([1,2,3], [2,3,4,5]));
        29
  •  1
  •   user1046987    6 年前

    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
  •  1
  •   jota3    6 年前

    自ECMA 2016以来,您可以使用:

    const intersection = (arr1, arr2) => arr1.filter(el => arr2.includes(el));