资讯动态

前端数组去重实战:从Set到Map的高效方案

发布时间:2026/11/6 5:46:32 来源:尧图企业网站定制
1. 前端数组去重从基础到高阶的实战指南数组去重是前端开发中最基础却又最常遇到的问题之一。很多开发者习惯性地使用[...new Set(arr)]解决所有场景但在实际业务中数据规模、对象复杂度、性能要求等因素都会影响方案选择。本文将结合具体业务场景分享我在电商平台开发中积累的数组去重经验。2. 基础去重方案与适用场景2.1 Set数据结构的基础应用ES6引入的Set对象是最简单的去重方案const uniqueArray [...new Set(originalArray)];原理剖析Set对象存储唯一值自动过滤重复项。扩展运算符...将其转换回数组。典型场景商品分类标签去重如[家电,数码,家电,服饰]用户选择器选项去重简单字符串/数字数组处理性能实测在Chrome浏览器测试10,000个随机数去重仅需0.5ms左右但存在以下局限无法处理对象引用{} {}返回false大数据量时内存消耗显著增加无法控制保留重复项的顺序提示在Vue/React的computed属性中使用Set去重时建议添加缓存机制避免重复计算。2.2 filterindexOf的传统方案arr.filter((item, index) arr.indexOf(item) index);虽然代码直观但存在O(n²)时间复杂度问题。实测10,000个元素需要200ms以上不推荐在现代项目中使用。3. 复杂对象去重实战3.1 基于Map的键值去重当处理对象数组时需要指定唯一标识属性function uniqueByKey(arr, key) { const seen new Map(); return arr.filter(item { const identifier item[key]; return !seen.has(identifier) seen.set(identifier, true); }); } // 使用示例 const orders [ {orderId: A100, product: Phone}, {orderId: A101, product: Laptop}, {orderId: A100, product: Tablet} ]; uniqueByKey(orders, orderId); // 结果保留前两个订单优化技巧使用Map而非普通对象支持非字符串键采用短路求值避免二次查找添加类型校验增强鲁棒性3.2 多条件复合去重实际业务中常需要多个字段组合作为唯一标识function uniqueByKeys(arr, keys) { const seen new Set(); return arr.filter(item { const compositeKey keys.map(k item[k]).join(|); return !seen.has(compositeKey) seen.add(compositeKey); }); } // 示例按category和brand去重 const products [ {id:1, category:phone, brand:Apple}, {id:2, category:phone, brand:Samsung}, {id:3, category:phone, brand:Apple} // 重复 ];4. 高性能去重方案4.1 排序预处理法对于10万级别的数据可先排序后去重function sortAndUnique(arr) { arr.sort(); // 可传入自定义排序函数 return arr.filter((item, i) i 0 || item ! arr[i-1]); }性能对比方法10万数据耗时内存占用Set120ms高排序法80ms低注意事项排序会改变原数组顺序对象数组需要自定义比较函数适合日志分析等顺序无关场景4.2 位图法去重适用于有限整数集合如用户IDfunction bitmapUnique(arr) { const bitmap []; return arr.filter(num { const bytePos num 3; const bitPos num % 8; const mask 1 bitPos; if (bitmap[bytePos] mask) return false; bitmap[bytePos] | mask; return true; }); }5. 特殊业务场景处理5.1 保留最后出现的重复项电商购物车合并场景常需保留最新添加的商品function keepLast(arr, key) { const seen {}; // 反向遍历 return arr.reverse().filter(item { const id item[key]; return seen[id] ? false : (seen[id] true); }).reverse(); // 恢复原顺序 }5.2 自定义合并逻辑更复杂的场景需要合并重复项而非简单丢弃function mergeDuplicates(arr, key, mergeFn) { const map new Map(); arr.forEach(item { const id item[key]; map.set(id, map.has(id) ? mergeFn(map.get(id), item) : item); }); return [...map.values()]; } // 示例合并订单金额 mergeDuplicates(orders, orderId, (existing, current) ({ ...existing, amount: existing.amount current.amount }));6. 性能优化与边界处理6.1 内存优化技巧处理超大数组时使用对象池复用临时对象分批处理分页/lazy loadingWeb Worker避免阻塞UI6.2 类型安全增强生产环境应添加防御性代码function safeUnique(arr, key) { if (!Array.isArray(arr)) return []; if (arr.some(item item[key] undefined)) { console.warn(Invalid key for uniqueness); return arr; } // ...正常去重逻辑 }7. 框架最佳实践7.1 Vue中的响应式去重// 在computed中使用 computed: { uniqueItems() { return this.keepLast(this.rawItems, id); } } // 使用memorize避免重复计算 import { memorize } from lodash-es; this.uniqueItems memorize(uniqueByKey);7.2 React性能优化// 使用useMemo缓存结果 const uniqueItems useMemo( () uniqueByKey(rawItems, id), [rawItems] ); // 自定义hook封装 function useUnique(items, key) { return useMemo(() { const map new Map(); return items.filter(item !map.has(item[key]) map.set(item[key], true)); }, [items, key]); }8. 实际案例解析8.1 电商平台商品筛选需求从不同渠道合并商品列表要求按SKU去重保留价格最低的版本合并库存数量const mergedProducts mergeDuplicates( products, sku, (existing, current) ({ ...existing, price: Math.min(existing.price, current.price), stock: existing.stock current.stock }) );8.2 数据分析场景处理用户行为日志时需要按sessionId去重保留最后一条记录处理可能的数据损坏function cleanLogs(logs) { return logs .filter(log log.sessionId) // 过滤无效数据 .reduceRight((acc, log) { // 从右向左遍历 if (!acc.seen.has(log.sessionId)) { acc.seen.add(log.sessionId); acc.result.push(log); } return acc; }, { seen: new Set(), result: [] }) .result .reverse(); // 恢复时间顺序 }9. 测试与调试技巧9.1 单元测试要点describe(uniqueByKey, () { it(应处理空数组, () { expect(uniqueByKey([], id)).toEqual([]); }); it(应处理undefined键值, () { const arr [{id:1}, {noId:2}, {id:3}]; expect(uniqueByKey(arr, id).length).toBe(2); }); it(应保持原数组顺序, () { const arr [{id:3}, {id:1}, {id:3}, {id:2}]; expect(uniqueByKey(arr, id).map(i i.id)).toEqual([3,1,2]); }); });9.2 性能测试方法// 生成测试数据 const largeArray Array(1e6).fill().map((_,i) ({ id: i % 1000, // 制造重复 value: Math.random() })); // 测试套件 console.time(Set去重); [...new Set(largeArray.map(JSON.stringify))].map(JSON.parse); console.timeEnd(Set去重); console.time(Map去重); uniqueByKey(largeArray, id); console.timeEnd(Map去重);10. 工程化实践建议工具函数封装将常用去重方法封装为团队工具库TS类型支持为不同场景添加精确的类型定义文档规范明确每种方法的适用场景和性能特征异常监控对可能的大数据操作添加性能监控// TypeScript增强 interface UniqueOptionsT { keep?: first | last; merge?: (existing: T, current: T) T; } function advancedUniqueT( arr: T[], key: keyof T, options?: UniqueOptionsT ): T[] { // 实现... }在大型前端架构中合理的去重策略能显著提升应用性能。我曾在一个商品推荐模块中通过优化去重算法将渲染性能提升了3倍。关键在于理解业务需求选择时间复杂度与空间复杂度平衡的方案而非盲目追求代码简洁。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑