简体   繁体   English

如何计算 JavaScript 中多个 arrays 的交集? [equals: function] 是什么意思?

[英]How to calculate intersection of multiple arrays in JavaScript? And what does [equals: function] mean?

I am aware of this question, simplest code for array intersection but all the solutions presume the number of arrays is two, which cannot be certain in my case.我知道这个问题,最简单的数组交集代码,但所有解决方案都假定 arrays 的数量是两个,在我的情况下不能确定。

I have divs on a page with data that contains arrays.我的页面上有包含 arrays 数据的 div。 I want to find the values common to all arrays.我想找到所有 arrays 共有的值。 I do not know how many divs/arrays I will have in advance.我不知道我将提前拥有多少个 div/数组。 What is the best way to calculate values common to all arrays?计算所有 arrays 共有值的最佳方法是什么?

var array1 = ["Lorem", "ipsum", "dolor"];
var array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
var array3 = ["Jumps", "Over", "Lazy", "Lorem"];
var array4 = [1337, 420, 666, "Lorem"];
//Result should be ["Lorem"];

I found another solution elsewhere, using Underscore.js.我在其他地方找到了另一个解决方案,使用 Underscore.js。

var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
_.intersection.apply(_, arrayOfArrays)
//Result is [43]

I've tested this with simple dummy data at my end and it seems to work.我已经用简单的虚拟数据对此进行了测试,它似乎有效。 But for some reason, some of the arrays I'm producing, which contain simple strings, also automatically include an added value, "equals: function":但由于某种原因,我正在生产的一些 arrays 包含简单的字符串,也自动包含一个附加值,“等于:函数”:

["Dummy1", "Dummy2", "Dummy3", equals: function]

And whenever I use the Underscore.js intersection method, on an array of arrays, I always get [equals: function] in dev tools, and not - if "Dummy3" is common to all arrays - ["Dummy3"].每当我使用 Underscore.js 交集方法时,在 arrays 数组上,我总是在开发工具中得到 [equals: function],而不是 - 如果“Dummy3”对所有 arrays - [“Dummy3”] 是通用的。

So TL;DR is there another solution to array intersection that would suit my case?所以 TL;DR 是否有另一种适合我的情况的数组交集解决方案? And can anyone explain what [equals: function] means here?任何人都可以解释 [equals: function] 在这里的含义吗? When I expand the item in the dev tools, it produces an empty array and a list of methods available on arrays (pop, push, shift etc), but these methods are all faded out, while equals: function is highlighted.当我在开发工具中展开项目时,它会生成一个空数组和 arrays 上可用的方法列表(pop、push、shift 等),但这些方法都淡出,而等于:function 突出显示。

You could just use Array#reduce with Array#filter and Array#includes .您可以将Array#reduceArray#filterArray#includes

 var array1 = ["Lorem", "ipsum", "dolor"], array2 = ["Lorem", "ipsum", "quick", "brown", "foo"], array3 = ["Jumps", "Over", "Lazy", "Lorem"], array4 = [1337, 420, 666, "Lorem"], data = [array1, array2, array3, array4], result = data.reduce((a, b) => a.filter(c => b.includes(c))); console.log(result);

I wrote a helper function for this:我为此编写了一个辅助函数:

function intersection() {
  var result = [];
  var lists;

  if(arguments.length === 1) {
    lists = arguments[0];
  } else {
    lists = arguments;
  }

  for(var i = 0; i < lists.length; i++) {
    var currentList = lists[i];
    for(var y = 0; y < currentList.length; y++) {
        var currentValue = currentList[y];
      if(result.indexOf(currentValue) === -1) {
        var existsInAll = true;
        for(var x = 0; x < lists.length; x++) {
          if(lists[x].indexOf(currentValue) === -1) {
            existsInAll = false;
            break;
          }
        }
        if(existsInAll) {
          result.push(currentValue);
        }
      }
    }
  }
  return result;
}

Use it like this:像这样使用它:

intersection(array1, array2, array3, array4); //["Lorem"]

Or like this:或者像这样:

intersection([array1, array2, array3, array4]); //["Lorem"]

Full code here完整代码在这里

UPDATE 1更新 1

A slightly smaller implementation here using filter这里使用filter稍微小一点的实现

This can be done pretty succinctly if you fancy employing some recursion and the new ES2015 syntax:如果您喜欢使用一些递归和新的 ES2015 语法,这可以非常简洁地完成:

 const array1 = ["Lorem", "ipsum", "dolor"]; const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"]; const array3 = ["Jumps", "Over", "Lazy", "Lorem"]; const array4 = [1337, 420, 666, "Lorem"]; const arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]]; // Filter xs where, for a given x, there exists some y in ys where y === x. const intersect2 = (xs,ys) => xs.filter(x => ys.some(y => y === x)); // When there is only one array left, return it (the termination condition // of the recursion). Otherwise first find the intersection of the first // two arrays (intersect2), then repeat the whole process for that result // combined with the remaining arrays (intersect). Thus the number of arrays // passed as arguments to intersect is reduced by one each time, until // there is only one array remaining. const intersect = (xs,ys,...rest) => ys === undefined ? xs : intersect(intersect2(xs,ys),...rest); console.log(intersect(array1, array2, array3, array4)); console.log(intersect(...arrayOfArrays)); // Alternatively, in old money, var intersect2ES5 = function (xs, ys) { return xs.filter(function (x) { return ys.some(function (y) { return y === x; }); }); }; // Changed slightly from above, to take a single array of arrays, // which matches the underscore.js approach in the Q., and is better anyhow. var intersectES5 = function (zss) { var xs = zss[0]; var ys = zss[1]; var rest = zss.slice(2); if (ys === undefined) { return xs; } return intersectES5([intersect2ES5(xs, ys)].concat(rest)); }; console.log(intersectES5([array1, array2, array3, array4])); console.log(intersectES5(arrayOfArrays));

Using a combination of ideas from several contributors and the latest ES6 goodness, I arrived at结合几个贡献者的想法和最新的 ES6 优点,我得到了

 const array1 = ["Lorem", "ipsum", "dolor"]; const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"]; const array3 = ["Jumps", "Over", "Lazy", "Lorem"]; const array4 = [1337, 420, 666, "Lorem"]; Array.prototype.intersect = function intersect(a, ...b) { const c = function (a, b) { b = new Set(b); return a.filter((a) => b.has(a)); }; return undefined === a ? this : intersect.call(c(this, a), ...b); }; console.log(array1.intersect(array2, array3, array4)); // ["Lorem"]

For anyone confused by this in the future,对于未来对此感到困惑的任何人,

_.intersection.apply(_, arrayOfArrays)

Is in fact the most elegant way to do this.实际上是最优雅的方式来做到这一点。 But:但:

var arrayOfArrays = [[43, 34343, 23232], [43, 314159, 343], [43, 243]];
arrayOfArrays = _.intersection.apply(_, arrayOfArrays);

Will not work!不管用! Must do必须做

var differentVariableName = _.intersection.apply(_,arrayOfArrays);

Small recursive divide and conquer solution that does not rely on es6 or any library.不依赖于 es6 或任何库的小型递归分治解决方案。

It accepts an array of arrays which makes the code shorter and allows you to pass arguments by using map.它接受一个数组数组,这使得代码更短,并允许您使用 map 传递参数。

 function intersection(a) { if (a.length > 2) return intersection([intersection(a.slice(0, a.length / 2)), intersection(a.slice(a.length / 2))]); if (a.length == 1) return a[0]; return a[0].filter(function(item) { return a[1].indexOf(item) !== -1; }); } var list1 = [ 'a', 'b', 'c' ]; var list2 = [ 'd', 'b', 'e' ]; var list3 = [ 'f', 'b', 'e' ]; console.log(intersection([list1, list2, list3]));

If you can use ES6 Maps and your arrays items are scalar values (easily usable as Map keys), then you can try this (works in my case) :如果您可以使用 ES6 Maps 并且您的数组项是标量值(很容易用作 Map 键),那么您可以尝试这个(在我的情况下有效):

const intersect_lists = (lists) => {
    const results = []
    const lookup = new Map()

    lists.map((list, idx) => {
        list.map((element) => {
            const count = lookup.get(element) || 0

            if(count === idx) {
                lookup.set(element, 1 + count)
            } else {
                lookup.delete(element)
            }
        })
    })

    // only elements present in all lists will have 
    // their respective counter equllling the total number of lists
    Array.from(lookup.keys()).map((key) => {
        if(lookup.get(key) === lists.length) {
            results.push(key)
        }
    })

    return results
}

Optionally you can pre-sort "lists" (of lists) by creasing length to avoid lots of iterations of the outer map() call, especially if lists lengths are heterogenous :或者,您可以通过增加长度来对“列表”(列表)进行预排序,以避免外部 map() 调用的大量迭代,特别是如果列表长度是异类的:

lists.sort((l1, l2) => l1.length - l2.length).map((list, idx) => { ... })

Your code with _lodash is working fine.您使用 _lodash 的代码运行良好。

As you can say in this fiddle:正如你在这个小提琴中所说的那样:

this code:这段代码:

var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
var a = _.intersection.apply(_, arrayOfArrays);
console.log(a);
console.log(a.length);

Will have output:将有输出:

[42]
1

Maybe you see也许你看到

equals: function等于:函数

because you are using kind of debugger.因为您正在使用某种调试器。

Try to just print the array with console.log , you will get only 42.尝试只用console.log打印数组,你只会得到 42。

Lodash pure:洛达什纯:

_.keys(_.pickBy(_.groupBy(_.flatten(arrays)), function (e) {return e.length > 1}))

Lodash with plain js:带有普通 js 的 Lodash:

var elements = {}, duplicates = {};
 _.each(arrays, function (array) {
     _.each(array, function (element) {
         if (!elements[element]) {
             elements[element] = true;
         } else {
             duplicates[element] = true;
         }
     });
 });
_.keys(duplicates);

Arg0n's answer is great, but just in case anyone is looking for intersection of object arrays, i modified Arg0n's answer a bit to deal with it. Arg0n 的答案很好,但以防万一有人在寻找对象数组的交集,我稍微修改了 Arg0n 的答案以解决它。

function getIntersectedData() {
    var lists = [[{a:1,b:2},{a:2,b:2}],[{a:1,b:2},{a:3,b:3}],[{a:1,b:2},{a:4,b:4}]];
    var result = [];
    for (var i = 0; i < lists.length; i++) {
        var currentList = lists[i];
        for (var y = 0; y < currentList.length; y++) {
            var currentValue = currentList[y];
            if (customIndexOf(result,currentValue)) {
                var existsInAll = true;
                for (var x = 0; x < lists.length; x++) {
                    if(customIndexOf(lists[x],currentValue)){
                        existsInAll = false;
                        break;
                    }
                }
                if (existsInAll) {
                    result.push(currentValue);
                }
            }
        }
        return result;
    }
}

function customIndexOf(array,value){
    var notFind = true;
    array.forEach(function(element){
        if(element.a === value.a){
            notFind = false;
        }
    });
    return notFind;
}

I manage to accomplish this with a reduce call:我设法通过reduce调用来完成此操作:

 var intersected = intersect([[1, 2, 3], [2, 3, 4], [3, 4, 5]]); console.log(intersected); // [3] function intersect(arrays) { if (0 === arrays.length) { return []; } return arrays.reduce((intersection, array) => { return intersection.filter(intersectedItem => array.some(item => intersectedItem === item)); }, arrays[0]); }

Intersection of a variable number of arrays.可变数量数组的交集。

This is how I do it:这就是我的做法:

function getArraysIntersection(list1, list2, ...otherLists) {
  const result = [];

  for (let i = 0; i < list1.length; i++) {
      let item1 = list1[i];
      let found = false;
      for (var j = 0; j < list2.length && !found; j++) {
          found = item1 === list2[j];
      }
      if (found === true) {
          result.push(item1);
      }
  }
  if (otherLists.length) {
    return getArraysIntersection(result, otherLists.shift(), ...otherLists);
  }
  else {
    return result;
  }
}

SNIPPET片段

 function getArraysIntersection(list1, list2, ...otherLists) { const result = []; for (let i = 0; i < list1.length; i++) { let item1 = list1[i]; let found = false; for (var j = 0; j < list2.length && !found; j++) { found = item1 === list2[j]; } if (found === true) { result.push(item1); } } if (otherLists.length) { return getArraysIntersection(result, otherLists.shift(), ...otherLists); } else { return result; } } const a = {label: "a", value: "value_A"}; const b = {label: "b", value: "value_B"}; const c = {label: "c", value: "value_C"}; const d = {label: "d", value: "value_D"}; const e = {label: "e", value: "value_E"}; const arr1 = [a,b,c]; const arr2 = [a,b,c]; const arr3 = [c]; const t0 = performance.now(); const intersection = getArraysIntersection(arr1,arr2,arr3); const t1 = performance.now(); console.log('This took t1-t0: ' + (t1-t0).toFixed(2) + ' ms'); console.log(intersection);

const intersect = (arrayA, arrayB) => {
    return arrayA.filter(elem => arrayB.includes(elem));
};
const intersectAll = (...arrays) => {
    if (!Array.isArray(arrays) || arrays.length === 0) return [];
    if (arrays.length === 1) return arrays[0];
    return intersectAll(intersect(arrays[0], arrays[1]), ...arrays.slice(2));
};

The cleanest way I've found to do this wasn't actually listed on this page, so here you are:我发现的最干净的方法实际上没有列在这个页面上,所以你在这里:

arrays[0].filter(elem => arrays.every(array => array.includes(elem)))

Reads like nice, clear english: every array includes the element.读起来像漂亮、清晰的英语:每个数组都包含元素。 It assumes that you have at least 1 element in arrays, though.不过,它假设您在数组中至少有 1 个元素。 If you can't make this assumption, you can use optional chaining:如果你不能做出这个假设,你可以使用可选链接:

arrays?[0].filter(elem => arrays.every(array => array.includes(elem))) ?? []

For anyone who might need, this implements the intersection inside an array of arrays:对于可能需要的任何人,这实现了数组数组内的交集:

intersection(array) {
  if (array.length === 1)
    return array[0];
  else {
    array[1] = array[0].filter(value => array[1].includes(value));
    array.shift();
    return intersection(array);
  }
}
function getIntersection(ar1,ar2,...arrays){
    if(!ar2) return ar1

    let intersection = ar1.filter(value => ar2.includes(value));

    if(arrays.length ===0 ) return intersection

    return getIntersection(intersection,...arrays)
}

console.log(getIntersection([1,2,3], [3,4], [5,6,3]) // [3]

Sol with Maps溶胶与地图
// nums1 = [1,2,2,1], nums2 = [2,2] // nums1 = [1,2,2,1], nums2 = [2,2]

// n  m
// O(nm) + space O(min(n, m))

// preprocess nums2 to a Map<number, count>
// O(n + m) + space(min(n, m))
// process the shorter one

let preprocessTarget = nums1
let loopTarget = nums2

if (nums1.length > nums2.length) {
  preprocessTarget = nums2
  loopTarget = nums1
}

// Map<element, number>
const countMap = new Map()
for (let num of preprocessTarget) {
  if (countMap.has(num)) {
    countMap.set(num, countMap.get(num) + 1)
  } else {
    countMap.set(num, 1)
  }
}


const result = []

for (let num of loopTarget) {
  if (countMap.has(num)) {
    result.push(num)
    
    const count = countMap.get(num)
    if (count === 1) {
      countMap.delete(num)
    } else {
      countMap.set(num, count - 1)
    }
  }
}

return result

Function to calculate intersection of multiple arrays in JavaScript Write a method that creates an array of unique values that are included in all given arrays. Function 计算 JavaScript 中多个 arrays 的交集编写一个创建唯一值数组的方法,该数组包含在所有给定的 arrays 中Expected Result: ([1, 2], [2, 3]) => [2]预期结果:([1, 2], [2, 3]) => [2]

 const arr1 = [1, 2, 1, 2, 1, 2]; const arr2 = [2, 3]; const arr3 = ["a", "b"]; const arr4 = ["b", "c"]; const arr5 = ["b", "e", "c"]; const arr6 = ["b", "b", "e"]; const arr7 = ["b", "c", "e"]; const arr8 = ["b", "e", "c"]; const intersection = (...arrays) => { (data = [...arrays]), (result = data.reduce((a, b) => a.filter((c) => b.includes(c)))); return [...new Set(result)]; }; console.log(intersection(arr1, arr2)); // [2] console.log(intersection(arr3, arr4, arr5)); // ['b'] console.log(intersection(arr5, arr6, arr7, arr8)); // ['b', 'e']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM