如何优化50万+数组的Map与Filter去重逻辑以提升性能?
大数组Map转换+去重的性能优化方案
问题场景
手里有一个包含50万+条数据的数组,需要先对数组做Map转换提取指定字段,再筛选出唯一值,同时过滤空值。目前的两种实现方式在大数据量下性能极差,执行一次需要5-10分钟:
现有实现1:Filter + IndexOf
const getUniqueValues = (array: string[]): string[] => { return array.filter((item, index, _array) => _array.indexOf(item) === index); }; const uniqueValues = getUniqueValues( editedData.map((bodyItem: any) => bodyItem[index]) ).filter(Boolean);
现有实现2:Reduce + Includes
const uniqueValues = editedData.reduce( (accumulator, bodyItem) => { const item = bodyItem[index]; if (!accumulator.includes(item)) { accumulator.push(item); } return accumulator; }, [] );
性能瓶颈分析
这两种方案慢的核心原因都是线性查找带来的O(n²)时间复杂度:
- 方案1先通过Map生成50万+条的临时数组,再用Filter遍历每个元素,每次调用
indexOf都要从头遍历数组查找当前元素,总操作次数约为50万×50万=2.5e11次。 - 方案2用Reduce遍历原数组,但每次判断
accumulator.includes(item)时,都要遍历已收集的去重数组(最终长度21万),总操作次数约为50万×21万=1.05e11次,运算量远超硬件处理能力。
优化方案:利用Set实现O(n)时间复杂度
Set的add和has方法都是O(1)常数时间操作,我们可以在一次遍历中完成字段提取、空值过滤和去重,彻底降低时间复杂度:
优化实现1:Reduce + Set
const uniqueValues = Array.from( editedData.reduce((valueSet, bodyItem) => { const item = bodyItem[index]; if (item) { // 过滤空值 valueSet.add(item); } return valueSet; }, new Set<string>()) );
优化实现2:普通for循环(性能略优)
如果追求极致性能,普通for循环的运行开销比Reduce略低:
const valueSet = new Set<string>(); for (const bodyItem of editedData) { const item = bodyItem[index]; if (item) { valueSet.add(item); } } const uniqueValues = Array.from(valueSet);
优化说明
- 时间复杂度从O(n²)降到O(n):只需要遍历原数组一次,每次操作都是常数时间,50万条数据的总操作次数仅为50万次左右,性能提升几个数量级。
- 减少内存占用:避免了生成50万+条的临时Map数组,直接用Set存储去重后的值(21万条),内存占用更低。
- 保留顺序:ES6+的Set会保留元素的插入顺序,转换为数组后,元素顺序和原数组中首次出现的顺序一致,和原有方案的结果顺序完全兼容。
对原有Reduce方案的修正
你之前写的Reduce逻辑是正确的,只是查找方式效率低,把数组替换成Set即可优化:
const uniqueValues = Array.from( editedData.reduce((accumulator, bodyItem) => { const item = bodyItem[index]; if (item && !accumulator.has(item)) { accumulator.add(item); } return accumulator; }, new Set<string>()) );
内容的提问来源于stack exchange,提问作者merlin
相关产品推荐
相关产品推荐

