[英]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#reduce
与Array#filter
和Array#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.