JavaScript数组去重终极指南:从基础到高级的多种方法(附面试题解析)

在前端开发中,数组去重是JavaScript中最常见的需求之一。本文将全面解析8种数组去重方法,包括基础实现、ES6新特性、性能优化等,并附上面试常见问题解析。


一、基础去重方法(ES5及以前)
  1. 双重循环法
    最基础的去重方法,通过两层循环比较元素:

    function unique(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;
    }// 示例
    console.log(unique([1, 2, 2, 3])); // [1, 2, 3]
    

    时间复杂度:O(n²)
    适用场景:小型数组,兼容性要求高

  2. indexOf优化法
    使用indexOf替代内层循环:

    function unique(arr) {const result = [];for (let i = 0; i < arr.length; i++) {if (result.indexOf(arr[i]) === -1) {result.push(arr[i]);}}return result;
    }
    

    注意indexOf内部也是循环,本质上还是O(n²)复杂度

  3. 排序相邻比较法
    先排序后比较相邻元素:

    function unique(arr) {arr.sort();const result = [arr[0]];for (let i = 1; i < arr.length; i++) {if (arr[i] !== arr[i-1]) {result.push(arr[i]);}}return result;
    }
    

    缺点:会改变原数组顺序,不适用于对象类型


二、ES6+ 高效去重方法
  1. Set数据结构法(最常用)
    ES6的Set自动处理唯一值:

    const unique = arr => [...new Set(arr)];// 示例
    console.log(unique([1, 2, 2, '2', NaN, NaN])); 
    // [1, 2, "2", NaN]
    

    特点

    • 代码最简洁(9个字符最短实现)
    • 支持NaN去重(NaN === NaN为false,但Set能识别)
    • 时间复杂度和空间复杂度均为O(n)
  2. Map数据结构法
    利用Map的键唯一性:

    function unique(arr) {const map = new Map();return arr.filter(item => !map.has(item) && map.set(item, true));
    }
    

    优势:保留原始顺序,适合需要顺序的场景

  3. filter + indexOf
    利用索引位置判断:

    const unique = arr => arr.filter((item, index) => arr.indexOf(item) === index);
    

    缺点indexOf无法识别NaN,时间复杂度O(n²)

  4. reduce累积法
    使用reduce实现优雅去重:

    const unique = arr => arr.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []
    );
    

三、特殊场景处理
  1. 对象数组去重
    根据对象属性去重:

    function uniqueByKey(arr, key) {const map = new Map();return arr.filter(item => {const identifier = item[key];return !map.has(identifier) && map.set(identifier, true);});
    }// 示例
    const users = [{id: 1, name: 'Alice'},{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}
    ];
    console.log(uniqueByKey(users, 'id')); 
    // 两个Alice对象只会保留一个
    
  2. 多维数组去重
    递归处理嵌套数组:

    function deepUnique(arr) {const flatArr = arr.flat(Infinity);return [...new Set(flatArr)];
    }// 示例
    console.log(deepUnique([1, [2, [3, 2]], 4])); // [1, 2, 3, 4]
    
  3. 混合类型精确去重
    区分数字1和字符串’1’:

    function strictUnique(arr) {const seen = new Map();return arr.filter(item => {const type = typeof item;const key = `${type}-${item}`;return !seen.has(key) && seen.set(key, true);});
    }// 示例
    console.log(strictUnique([1, '1', 2, 2])); // [1, "1", 2]
    

四、面试高频问题与陷阱解析
  1. 问题:Set去重有什么缺陷?

    • 不区分-0+0(Set认为它们相等)
    • 对象内容相同但引用不同时无法去重
    const obj = {a: 1};
    console.log(unique([obj, obj, {a: 1}])); 
    // [obj, {a:1}] → 两个不同对象
    
  2. 问题:如何实现O(n)时间复杂度的去重?
    :使用Set或Map数据结构,它们的查找操作是O(1)复杂度

  3. 陷阱:indexOf无法识别NaN

    [NaN].indexOf(NaN) // -1 → 无法识别
    [...new Set([NaN, NaN])] // [NaN] → 正确识别
    
  4. 问题:如何保留去重后的原始顺序?
    :使用Map或对象记录首次出现位置:

    function orderedUnique(arr) {return [...new Map(arr.map(item => [item, item])).values()];
    }
    
  5. 性能大比拼(10,000个元素测试):

    方法耗时(ms)可读性适用场景
    双重循环1200★★☆小型数组
    Set2.5★★★★★现代浏览器
    filter + indexOf850★★★☆简单去重
    Map3.0★★★★☆需保留顺序的场景

五、去重方法总结表
方法代码复杂度时间复杂度特殊类型支持适用场景
双重循环O(n²)所有类型兼容性要求高的老项目
Set极低O(n)✅ NaN现代项目首选
MapO(n)✅ 对象引用需要保留顺序的场景
filter + indexOfO(n²)❌ NaN简单数组去重
reduceO(n²)❌ NaN函数式编程场景
排序相邻比较O(n log n)❌ 混合类型纯数字/字符串数组
对象属性去重O(n)✅ 按指定属性去重对象数组去重

六、最佳实践与使用建议
  1. 现代项目首选

    // 99%场景使用Set足够
    const unique = arr => [...new Set(arr)];
    
  2. 需要兼容IE时

    // 使用对象+类型标记的polyfill
    function legacyUnique(arr) {const seen = {};return arr.filter(item => {const key = typeof item + JSON.stringify(item);return seen.hasOwnProperty(key) ? false : (seen[key] = true);});
    }
    
  3. 大数组优化
    对于超过10,000个元素的数组:

    function bigArrayUnique(arr) {const set = new Set();const result = [];for (let i = 0; i < arr.length; i++) {if (!set.has(arr[i])) {set.add(arr[i]);result.push(arr[i]);}}return result;
    }
    
  4. TypeScript强化版

    function typedUnique<T>(arr: T[]): T[] {return Array.from(new Set(arr));
    }// 对象数组按key去重
    function uniqueByKey<T>(arr: T[], key: keyof T): T[] {const map = new Map<any, T>();arr.forEach(item => {const identifier = item[key];if (!map.has(identifier)) {map.set(identifier, item);}});return Array.from(map.values());
    }
    

掌握这些去重方法,不仅能轻松应对面试,更能根据实际场景选择最优方案。记住:当简单方案(如Set)能满足需求时,不要过度设计。建议在项目中统一封装去重工具函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85172.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85172.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/85172.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于51单片机的智能小车:按键调速、障碍跟踪、红外循迹与数码管显示(一个合格的单片机课设)

引言 在嵌入式系统领域&#xff0c;51单片机因其简单易用、成本低廉的特点&#xff0c;一直是入门学习的理想平台。今天我将分享一个基于51单片机的多功能智能小车项目&#xff0c;它集成了按键PWM调速、障碍物跟踪、红外循迹和数码管显示四大功能。这个项目不仅涵盖了嵌入式开…

Java异常处理(try-catch-finally):像医生一样处理程序的“感冒”

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、从一个真实问题开始&#xff1a;为什么需要异常处理&#xff1f; 假设你正在开发一个文件读取工具&#xff0c;用户输入文件名后&#xff0c;程序会读…

PostgreSQL 数据库故障与性能高效实时监测技术深度解析

关键词&#xff1a; postgresql 故障与性能监控 &#x1f4d1; 文章目录 1. 引言与监控重要性 2. PostgreSQL监控体系架构 3. 故障监控核心技术 4. 性能监控关键指标 5. 实时监测技术实现 6. 监控工具选型与部署 7. 故障预警与自动化响应 8. 性能调优监控策略 9. 最佳…

logrotate 踩坑

我的logrotate配置&#xff0c;原本运行正常&#xff0c;最近几天发现轮转失败&#xff0c;两个目录下的日志全部无法轮转&#xff0c;于是开始排查问题 /data01/logs/test1/*.log /data01/logs/test2/*.log {missingokrotate 1notifemptycreate 0644 www-data admsharedscrip…

FastGPT、百度智能体、Coze与MaxKB四大智能体平台在政务场景下的深度对比

在生成式AI技术快速迭代的浪潮中&#xff0c;百度智能体平台、Coze、FastGPT和MaxKB作为四大智能体开发平台&#xff0c;凭借差异化的技术路径和功能特性&#xff0c;正在重塑政务AI应用的开发范式。本文从功能实现、政务场景适应性等维度展开深度解析&#xff0c;为开发者提供…

基于SpringBoot的美食分享平台-038

一、项目技术栈 Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;采用HTML和Vue相结合开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 …

【C++第三方包安装】Windows与Linux安装配置redis-plus-plus指南

前言 下面主要是对于两种环境安装、配置、使用C的第三方包&#xff08;redis&#xff09;&#xff0c;对于其他的第三方库&#xff0c;也可以使用类似的方法进行类比安装。 且大多数的第三方库都可以利用工具一键安装或手动编译安装。 Windows 要在Windows系统上快速安装和使…

springboot入门之路(二)

系列文章目录 springboot入门之路&#xff08;一&#xff09;连续的学习渐进之路。阅读点击&#xff1a;springboot入门之路(一) 文章目录 系列文章目录3.springboot配置及注意事项3.1继承starter parent3.2使用没有父POM的Spring Boot3.3配置java的编译的版本3.4使用"de…

【开源解析】基于Python+Qt打造智能应用时长统计工具 - 你的数字生活分析师

&#x1f4ca; 【开源解析】基于PythonQt打造智能应用时长统计工具 - 你的数字生活分析师 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情源自…

PHP语法基础篇(三):类型转换与常量

"在完成PHP输出函数和字符串操作的学习后&#xff0c;本篇笔记将记录 类型转换和 常量应用的学习过程。作为语法基础篇的第三部分&#xff0c;将重点关注&#xff1a; 类型转换数学函数常量定义&#xff1a;define() 与const 的使用差异魔术常量应用&#xff1a;__LINE__ …

Linux lsof 命令详解+实例

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

【Cobalt Strike手册】客户端界面功能

工具栏 顶部的工具栏提供了快速访问的功能&#xff0c;这些图片的功能从左到右功能以此如下表 创建新的连接断开当前的TeamServerListeners监听器列表以图形化展示表格形式展示表格展示目标管理Web服务查看获取到的认证信息查看下载的文件查看键盘记录查看截屏记录 图形化会话…

FastAPI本地文档的定制技巧

磨刀不误砍柴工&#xff0c;一份清晰的API文档能让前后端协作效率翻倍——源滚滚如是说 在前后端分离开发的今天&#xff0c;接口文档的质量直接决定了团队协作的效率。作为Python领域最受瞩目的现代Web框架&#xff0c;FastAPI最大的亮点之一是其自动化交互式文档功能。但很多…

Python 标准库概览

Python 标准库非常庞大,所提供的组件涉及范围十分广泛,使用标准库我们可以让您轻松地完成各种任务。 以下是一些 Python3 标准库中的模块: os 模块:os 模块提供了许多与操作系统交互的函数,例如创建、移动和删除文件和目录,以及访问环境变量等。 sys 模块:sys 模块提供…

AI大模型:(二)4.1 文生图(Text-to-Image)模型发展史

目录 1.介绍 2.发展历史 2.1.早期探索阶段(1980-2014 年) 2.1.1.卷积神经网络(CNN) 2.1.2.生成对抗网络(GAN)的提出 2.2.GAN主导时代(2015-2018 年) 2.2.1.高分辨率GAN的突破 2.2.2.文本-图像对齐的改进 2.3. Diffusion革命(2021–2022) 2.3.1.扩散模型(D…

vue3实现轮播渲染多张图每张进行放大缩小拖拽功能互不影响

vue3实现轮播渲染多张图每张进行放大缩小拖拽功能互不影响 1.以vue3中el-carousel轮播插件为例 <div class"pic_view"><el-carousel height"100vh" :autoplay"false" ref"carouselRef" change"handleCarouselChange&qu…

traceroute 使用说明

1、概述 Traceroute&#xff08;Windows 系统中为 tracert&#xff09;是一种网络诊断工具&#xff0c;用于跟踪数据包从本地设备到目标主机的传输路径&#xff0c;并显示沿途经过的每一跳&#xff08;路由器&#xff09;的延迟和 IP 地址。它通过发送不同 TTL&#xff08;生存…

用idea操作git缓存区回退、本地库回退、远程库回退

前言 使用idea软件操作git非常人性化和方便。 但是如果我的代码使用git提交之后,我想回到以前的版本,此时需要进行git的版本回退。 提交代码分为提交到缓存区、本地库、远程库这3个过程。 下面我将介绍每个阶段的提交对应的回退方法。 本篇文章是掌握git和使用idea操作git…

webpack+vite前端构建工具 - 3webpack处理js

3 webpack处理js webpack的核心——处理js文件&#xff0c;将模块化的代码打包。具体操作如下 es6转化&#xff08;为兼容老浏览器&#xff0c;将es6转化为es5&#xff09; babel-loader 代码规范&#xff08;例如空格&#xff0c;缩进等代码风格规范&#xff09; eslint 代码…

Nginx转发中相对路径资源302问题的分析与解决

Nginx转发中相对路径资源302问题的分析与解决 典型案例&#xff1a;后端页面引入./test.css的302问题 问题场景 假设我们有一个后端服务&#xff0c;其页面中通过相对路径引入了CSS文件&#xff1a; <!-- 后端页面代码 --> <link rel"stylesheet" href&…