繁体   English   中英

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

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

我知道这个问题,最简单的数组交集代码,但所有解决方案都假定 arrays 的数量是两个,在我的情况下不能确定。

我的页面上有包含 arrays 数据的 div。 我想找到所有 arrays 共有的值。 我不知道我将提前拥有多少个 div/数组。 计算所有 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"];

我在其他地方找到了另一个解决方案,使用 Underscore.js。

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

我已经用简单的虚拟数据对此进行了测试,它似乎有效。 但由于某种原因,我正在生产的一些 arrays 包含简单的字符串,也自动包含一个附加值,“等于:函数”:

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

每当我使用 Underscore.js 交集方法时,在 arrays 数组上,我总是在开发工具中得到 [equals: function],而不是 - 如果“Dummy3”对所有 arrays - [“Dummy3”] 是通用的。

所以 TL;DR 是否有另一种适合我的情况的数组交集解决方案? 任何人都可以解释 [equals: function] 在这里的含义吗? 当我在开发工具中展开项目时,它会生成一个空数组和 arrays 上可用的方法列表(pop、push、shift 等),但这些方法都淡出,而等于:function 突出显示。

您可以将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);

我为此编写了一个辅助函数:

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

像这样使用它:

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

或者像这样:

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

完整代码在这里

更新 1

这里使用filter稍微小一点的实现

如果您喜欢使用一些递归和新的 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));

结合几个贡献者的想法和最新的 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"]

对于未来对此感到困惑的任何人,

_.intersection.apply(_, arrayOfArrays)

实际上是最优雅的方式来做到这一点。 但:

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

不管用! 必须做

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

不依赖于 es6 或任何库的小型递归分治解决方案。

它接受一个数组数组,这使得代码更短,并允许您使用 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]));

如果您可以使用 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
}

或者,您可以通过增加长度来对“列表”(列表)进行预排序,以避免外部 map() 调用的大量迭代,特别是如果列表长度是异类的:

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

您使用 _lodash 的代码运行良好。

正如你在这个小提琴中所说的那样:

这段代码:

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

将有输出:

[42]
1

也许你看到

等于:函数

因为您正在使用某种调试器。

尝试只用console.log打印数组,你只会得到 42。

洛达什纯:

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

带有普通 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 的答案很好,但以防万一有人在寻找对象数组的交集,我稍微修改了 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;
}

我设法通过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]); }

可变数量数组的交集。

这就是我的做法:

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

片段

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

我发现的最干净的方法实际上没有列在这个页面上,所以你在这里:

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

读起来像漂亮、清晰的英语:每个数组都包含元素。 不过,它假设您在数组中至少有 1 个元素。 如果你不能做出这个假设,你可以使用可选链接:

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

对于可能需要的任何人,这实现了数组数组内的交集:

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]

溶胶与地图
// 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 计算 JavaScript 中多个 arrays 的交集编写一个创建唯一值数组的方法,该数组包含在所有给定的 arrays 中预期结果:([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