js数组去重多种实现方法
数组去重不只是 new Set():从原理到实战的避坑指南
前端日常开发里,数组去重简直像呼吸一样自然。无论是接口返回的重复标签、用户多选控件的脏数据,还是动态列表渲染前的清洗步骤,只要数据流过来,总得有个办法把“水”滤干净。别一上来就复制粘贴通用代码,不同场景下的去重逻辑,其实藏着不少细节坑。
现代项目里,[...new Set(arr)] 基本是首选方案。代码精简,底层基于哈希表实现,时间复杂度稳定在 O(n),而且天然保留原始顺序。但它对数据类型的判定极其严格,数字 1 和字符串 '1' 不会被合并,如果是来自多端接口的混合字段,直接丢进去很容易漏掉本该去重的项。这时候需要先做一层标准化处理,比如统一转成字符串或指定格式。
如果项目需要兼容老旧环境,或者你想更直观地看到筛选过程,arr.filter((item, index) => arr.indexOf(item) === index) 依然能打。核心逻辑是让 indexOf 锁定首次出现的位置,后续重复的直接拦截。这招对基础类型非常友好,但遇到对象或数组时,indexOf 比较的是内存引用地址,哪怕两个对象属性完全一致,也会被当成独立个体放行。纯业务标签列表用这一条足够稳妥。
偏爱函数式思维的开发人员,往往倾向 arr.reduce((acc, curr) => acc.includes(curr) ? acc : [...acc, curr], [])。它将去重拆解为累加器的累积动作,语义清晰,链式调用读起来毫无障碍。代价是性能会随着数据量呈二次方增长,includes 每次都要扫描已收集的结果集,上万个元素时容易拖慢主线程。适合中小规模列表或静态配置项。
真正面对万级以上的实时数据清洗时,对象键值映射法 才是隐形主力。利用键名唯一性建立哈希表:const seen = {}; return arr.filter(v => seen[v] ? false : (seen[v] = true))。不仅达到 O(1) 的检索速度,还能顺带吞掉 NaN、null、undefined 这些让其他方法头疼的特殊值。不过对象键在存入时会隐式转为字符串,遇到包含 [ 或 { 的键名可能引发覆盖冲突,生产环境建议拼接类型标识符兜底。
上述方案主要针对一维扁平结构。若数据里嵌套了对象或子数组,比如批量导入的联系人明细,直接套用只会留下空壳。此时可结合 JSON.stringify 序列化后再进 Set,快速压平结构;但需注意对象属性顺序不一致会被误判为不同记录,强一致性要求下,最好借助结构化比对工具或自定义深度相等函数。
数组去重看似是基础操作,却是检验代码取舍功力的试金石。面对具体需求,先厘清三个维度:数据量级、是否必须保序、是否存在类型混叠。选对底层机制,比盲目追求语法糖能少踩无数回网络请求与渲染瓶颈。下次再遇到数据扎堆的情况,不妨先问自己一句:这次要筛掉的,是表面重复的值,还是背后绑着业务规则的完整记录。把边界想透,写出的逻辑才经得起线上流量的反复冲刷。


还没有评论,来说两句吧...