编辑:误解了问题,它不是求交集,而是提取个别数组之间唯一的值(例如不是交集),为此可能会起作用:
let arr1 = ['a', 'b', 'c', 'a', 'b'];
let arr2 = ['a', 'd', 'b', 'c'];
let arr3 = ['f', 'c', 'a'];
const thereCanOnlyBeOne = function(...arrs) {
return Array.from(
arrs.reduce((map, arr) => {
new Set(arr).forEach((v) => map.set(v, map.has(v) ? map.get(v)+1 : 1));
return map;
}, new Map())
)
.filter(([value, count]) => count === 1)
.map(([value, count]) => value);
};
console.log(thereCanOnlyBeOne(arr1, arr2, arr3));
我认为@gog的答案更加复杂,可能会更快,但我有些难以理解(叫我蠢,我接受 =D,编辑:不得不进行一些研究,阅读/学习一些关于位集 在这里 和 在这里), 所以这是使用Map和数组方法略微复杂的方式的分解:
- 将要分析的所有数组传递到函数中,顺序无关紧要
- 循环(我选择reduce,但任何循环结构都可以)遍历所有输入数组及其值,在Map中计算出现次数,最后Map将如下所示:
0: {"a" => 4}
1: {"b" => 3}
2: {"c" => 3}
3: {"d" => 1}
4: {"f" => 1}
完成后,我们通过Array.from()
将Map转换回数组,创建一个元组数组:
[
["a", 4],
["b", 3],
["c", 3],
["d", 1],
["f", 1],
]
将现在形如
[<value>, <count>]
的元组数组过滤,只保留确切出现一次的值,得到:
[
["d", 1],
["f", 1],
]
- 对筛选后的数组进行映射,将其“简化”为一维数组,并返回结果:
["d", "f"]
警告:在内部,此代码会执行大量循环操作,因此也可以称之为暴力循环,尽管由于“性感”的ES6数组语法糖而看起来“更短”。
为了完整起见,稍作修改,因为可以通过迭代计数器Map一旦完成并简单地删除不具有值1的Map条目来省略Array.filter()
步骤(尽管似乎更快)。
let arr1 = ['a', 'b', 'c', 'a', 'b'];
let arr2 = ['a', 'd', 'b', 'c'];
let arr3 = ['f', 'c', 'a'];
const thereCanOnlyBeOne = function(...arrs) {
let result;
arrs
.reduce((map, arr) => {
new Set(arr).forEach((v) => map.set(v, map.has(v) ? map.get(v)+1 : 1));
return map;
}, new Map())
.forEach((value, key, map) => { value !== 1 && map.delete(key); result = map; });
return Array.from(result).map(([value, count]) => value);
};
console.log(thereCanOnlyBeOne(arr1, arr2, arr3));
更新:正如@Nick Parsons所指出的,代码的早期版本将不会输出仅存在于一个数组中但多次出现的元素。
如果一个数组包含相同的值,并且该元素不存在于任何其他数组中,则将产生错误的输出。例如,如果从arr2中删除b,则只有arr1中有b,而其他数组则没有,因此应在最终结果中包含b。
这可以通过将被检查的数组转换为 Set()
来轻松解决(从而将数组的值减少到“唯一”值)。
如果有人(除了我以外)想知道,这里是gog选项和我的基准测试,他的位集方法显然是最快的,因此,如果您正在比较小于32个数组,那么这是迄今为止最有效的解决方案:https://jsben.ch/YkKSu
如果有人喜欢gog的bitset实现的ES6版本(由@ralphmerridew提出的改进),这里是:
let arr1 = ['a', 'b', 'c', 'a', 'b'];
let arr2 = ['a', 'd', 'b', 'c'];
let arr3 = ['f', 'c', 'a'];
function onlyone(...arrays) {
return Object.entries(
arrays.reduce((map, arr, n) => {
arr.forEach((v) => map[v] = (map[v] ?? 0) | (1 << n));
return map;
}, {})
)
.filter(([value, bitmap]) => (bitmap & (bitmap-1)) == 0)
.map(([value, bitmap]) => value);
};
console.log(onlyone(arr1, arr2, arr3));
已更新基准测试,有趣的是(或出乎意料的是),这个看起来“较慢”的ES6实现在chrome和firefox中多次击败了gog的for循环实现,我自己都不敢相信,以为这些语法糖方法与for循环相比会稍微拖慢速度,好吧...知道就好了 =)
我还尝试使用BigInt()实现位集方法,以消除它只能处理32个数组的问题(取决于引擎,使用BigInt应该可以处理100万到10亿个数组),不幸的是,这似乎使它成为所有解决方案中最慢的一个(基准测试已更新):
let arr1 = ['a', 'b', 'c', 'a', 'b'];
let arr2 = ['a', 'd', 'b', 'c'];
let arr3 = ['f', 'c', 'a'];
function onlyoneBigInt(...arrays) {
return Object.entries(
arrays.reduce((map, arr, n) => {
arr.forEach((v) => map[v] = (map[v] ?? 0n) | (1n << BigInt(n)));
return map;
}, {})
)
.filter(([value, bitmap]) => (bitmap & (bitmap-1n)) == 0)
.map(([value, bitmap]) => value);
};
console.log(onlyoneBigInt(arr1, arr2, arr3));
也许有人看到了可以改进使其更快的东西?