当前位置: 首页 > news >正文

JavaScript 常用数组去重方法及性能对比(附:扩展运算符...总结)

JavaScript 数组去重有多种方法,性能差异显著。

以下是常用方法及其性能对比:


1. ES6 Set(推荐)

javascript

// 最快,代码最简洁 function uniqueSet(arr) { return [...new Set(arr)]; // 或 Array.from(new Set(arr)) }

2. Filter + indexOf

javascript

function uniqueFilter(arr) { return arr.filter((item, index) => arr.indexOf(item) === index); }

3. Reduce

javascript

function uniqueReduce(arr) { return arr.reduce((acc, cur) => { if (!acc.includes(cur)) { acc.push(cur); } return acc; }, []); }

4. 双层循环

javascript

function uniqueDoubleLoop(arr) { const result = []; for (let i = 0; i < arr.length; i++) { let isDuplicate = false; for (let j = 0; j < result.length; j++) { if (arr[i] === result[j]) { isDuplicate = true; break; } } if (!isDuplicate) result.push(arr[i]); } return result; }

5. Object键值(仅字符串)

javascript

function uniqueObject(arr) { const obj = {}; const result = []; for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { obj[arr[i]] = true; result.push(arr[i]); } } return result; }

性能对比测试

javascript

// 生成测试数据 const testArray = []; for (let i = 0; i < 10000; i++) { testArray.push(Math.floor(Math.random() * 1000)); } // 性能测试函数 function testPerformance(fn, arr, name) { const start = performance.now(); fn([...arr]); // 避免修改原数组 const end = performance.now(); console.log(`${name}: ${(end - start).toFixed(2)}ms`); } // 测试 console.log('10000个元素的数组去重性能对比:'); testPerformance(uniqueSet, testArray, 'Set'); testPerformance(uniqueFilter, testArray, 'Filter'); testPerformance(uniqueReduce, testArray, 'Reduce'); testPerformance(uniqueDoubleLoop, testArray, '双层循环'); testPerformance(uniqueObject, testArray, 'Object键值');

性能排名(从快到慢)

  1. Set- O(n) - 最快,代码最简洁

  2. Object键值- O(n) - 较快,但仅适用于字符串或可转字符串的值

  3. Filter + indexOf- O(n²) - 中等,代码简洁但性能随数据量下降

  4. Reduce- O(n²) - 中等偏慢

  5. 双层循环- O(n²) - 最慢


特殊场景考虑

处理复杂对象

javascript

// 对象数组去重(基于某个属性) function uniqueByKey(arr, key) { const map = new Map(); return arr.filter(item => { if (!map.has(item[key])) { map.set(item[key], true); return true; } return false; }); } // 使用JSON.stringify(性能较差) function uniqueObjects(arr) { const set = new Set(); return arr.filter(obj => { const str = JSON.stringify(obj); if (set.has(str)) return false; set.add(str); return true; }); }

保持顺序

大多数方法(Set、Filter、Reduce)都能保持原始顺序,但Object键值不保证顺序。


总结建议

方法推荐度适用场景
Set⭐⭐⭐⭐⭐绝大多数场景,ES6环境
Object键值⭐⭐⭐简单值且不关心类型转换
Filter⭐⭐小数组,简洁代码优先
Reduce⭐⭐需要复杂处理逻辑
双层循环学习算法时,实际开发不推荐

最佳实践:

  • 现代项目直接使用[...new Set(arr)]

  • 需要考虑兼容性时,使用polyfill或lodash的_.uniq()

  • 大型数组优先考虑时间复杂度O(n)的方法


扩展运算符(Spread Operator)...总结

类别语法描述示例
基本概念...iterable将可迭代对象展开为独立元素...array,...string
数组操作
复制数组const newArr = [...arr]浅拷贝数组const arr1 = [1,2,3]; const arr2 = [...arr1]
合并数组[...arr1, ...arr2]合并多个数组[1,2, ...arr, 4,5]
数组去重[...new Set(arr)]配合Set快速去重[...new Set([1,2,2,3])] // [1,2,3]
插入元素[first, ...rest]结合解构分离首元素const [first, ...others] = [1,2,3,4]
对象操作
复制对象const newObj = {...obj}浅拷贝对象属性const obj1 = {a:1}; const obj2 = {...obj1}
合并对象{...obj1, ...obj2}合并对象,后者覆盖前者{...defaults, ...options}
添加属性{...obj, newProp: value}添加新属性{...user, age: 25}
覆盖属性{...obj, prop: newValue}修改已有属性值{...user, name: 'Bob'}
函数应用
函数参数func(...args)将数组展开为函数参数Math.max(...[1,2,3])
剩余参数function(...args){}收集剩余参数为数组function sum(...nums){...}
字符串操作
字符串转数组[...str]正确处理Unicode字符[...'hello'] // ['h','e','l','l','o']
表情符号处理[...'👨‍👩‍👧']正确处理组合emoji[...'👍🏽'] // ['👍🏽']
可迭代对象
NodeList转数组[...document.querySelectorAll('div')]类数组转真正数组const divs = [...document.querySelectorAll('div')]
Arguments转数组[...arguments]函数arguments转数组function f(){ return [...arguments]; }
Map/Set转数组[...map],[...set]Map/Set转为数组[...new Set([1,2,2])] // [1,2]
组合使用
数组+解构[first, ...rest] = arr提取首元素和剩余数组const [head, ...tail] = [1,2,3,4]
对象+解构{a, ...rest} = obj提取指定属性和剩余属性const {id, ...data} = obj
嵌套展开[...arr1, mid, ...arr2]在任意位置展开数组[0, ...middle, 9]
注意事项
浅拷贝{...obj},[...arr]只拷贝一层,嵌套对象引用共享{...obj}不会深拷贝嵌套对象
不可迭代错误...123数字等不可迭代对象会报错...123 // TypeError
性能考虑大数组展开大数组展开可能影响性能避免在循环中频繁展开大数组
特殊技巧
条件展开{...obj, ...(cond && {prop: val})}条件添加属性{...obj, ...(show && {visible: true})}
移除属性{propToRemove, ...rest} = obj结合解构移除属性const {password, ...safeUser} = user
默认值+覆盖{...defaults, ...overrides}设置默认值并允许覆盖const config = {...defaultConfig, ...userConfig}

关键特点总结

  1. 浅拷贝:只复制一层,嵌套对象是引用

  2. 可迭代性:只能用于可迭代对象(数组、字符串、Set、Map等)

  3. 不可变操作:创建新对象/数组,不修改原数据

  4. 顺序重要:在对象合并中,后面的属性覆盖前面的

  5. ES6+特性:需要Babel转译以支持旧浏览器


实用示例

javascript

// 1. 快速数组合并去重 const uniqueMerge = [...new Set([...arr1, ...arr2])]; // 2. 带默认值的对象配置 const createConfig = (userConfig) => ({ port: 3000, host: 'localhost', ...userConfig }); // 3. 安全的删除属性 const removePassword = ({password, ...user}) => user; // 4. 数组插入元素 const insertAt = (arr, index, ...items) => [ ...arr.slice(0, index), ...items, ...arr.slice(index) ];

扩展运算符是现代JavaScript开发中最常用、最简洁的特性之一,极大简化了数组和对象的操作

http://www.cnnetsun.cn/news/92120.html

相关文章:

  • # 深入 Ascend C 内存模型:掌握UB、GM与流水线优化,打造极致AI算子
  • 冥想第一千七百三十五天(1735)
  • 代理IP和普通IP有什么区别?这篇文章帮你捋明白
  • 体系结构分类和指令系统
  • 基于AI数字人系统源码的低成本开发方案与实践经验
  • SQL 调优全解:从 20 秒到 200 ms 的 6 步实战笔记(附脚本)
  • YOLO目标检测模型如何对接Apipost平台
  • 简单的创建一个Spring Boot网页
  • 鼠标滚轮缩放图片:前端实现高清无损放大技巧(附实战代码)
  • Numpy库实践2_索引和数组的操作
  • 图解 - 红黑树(插入)
  • Memgraph 全新 AI 图工具包:一键构建 GraphRAG 聊天机器人,实现快速上下文感知响应
  • 初始化列表和特殊成员
  • (二)前端基础框架构建
  • vLLM推理引擎教程6-Nsight Systems性能分析
  • 基于MATLAB的燃料电池汽车参数匹配与能量管理策略优化及仿真模型研究资料库
  • AM247L-0000伺服电机
  • DoraemonKit(DoKit)使用教程:从集成到实战
  • 构筑 AI 理论体系:深度学习 100 篇论文解读 第十九篇:序列建模的焦点——注意力机制 Attention Mechanism (2015)
  • 【小白笔记】移除元素与删除有序数组中的重复项与轮转数组(三步反转)
  • 什么是关键字驱动测试?
  • 前沿技术借鉴研讨-2025.12.16(超声心动图综述/妊娠期糖尿病/降低CTG解读主观性)
  • 别让发成绩,耗掉你课后的半小时
  • 企业级 Prompt 管理中心:实验分流 + 曝光埋点 + 可回溯,版本化/AB/DSL/可观测全齐
  • 执行 install.sh 报错 `env: ‘bash\r‘: No such file or directory` 怎么解决?
  • Part 10|我给这套系统划的第一个边界
  • agent-zh.md
  • 为什么过滤 rtmpt 而不是 rtmp?
  • Navicat x 达梦技术指引 | 启用和配置AI助手
  • Transformer的注意力权重的理解