js如何实现数组去重

最高效的js数组去重方法是利用set对象,因其底层基于哈希表,平均时间复杂度为o(n),性能最优;2. filter结合indexof方法兼容性好但时间复杂度为o(n^2),适合小规模数组;3. reduce结合map同样具有o(n)时间复杂度,灵活性高,适用于需要自定义去重逻辑或处理对象数组的场景;4. 对于对象数组去重,需基于唯一标识属性(如id)或组合键使用filter加set/map,或利用map覆盖机制保留最后出现的元素;5. set、filter+indexof和reduce+map三种方法在现代javascript环境中均能保持元素的原始顺序,其中set在实际引擎实现中按插入顺序迭代,确保去重后顺序一致;综上,优先推荐使用set进行基本类型去重,在需要兼容性或复杂逻辑时选用reduce加map方案,对象数组则依据属性定义唯一性标准进行处理,所有方法均能有效保持原始顺序。

js如何实现数组去重

JS数组去重,说白了,就是把一个数组里重复的元素给剔除掉,只留下那些独一无二的。这事儿在前端开发里挺常见的,比如你从后端拿到一份数据列表,里面可能因为各种原因出现了重复项,这时候你就得想办法把它“洗干净”。选择哪种方法,其实挺看你的具体场景和对性能、代码简洁度的偏好。

利用Set对象去重是最现代也最直接的方式。Set是ES6引入的一种新的数据结构,它最大的特点就是成员的值都是唯一的。所以,把数组扔进Set里走一圈,再把它变回数组,基本上就搞定了。

const originalArray = [1, 2, 2, 'hello', 'world', 'hello', null, null, undefined, undefined, NaN, NaN, {}, {}];const uniqueArrayBySet = [...new Set(originalArray)];console.log(uniqueArrayBySet);// 结果:[1, 2, 'hello', 'world', null, undefined, NaN, {}, {}]// 注意:Set在处理NaN时,会认为所有NaN都是同一个值,但对于对象,它会认为两个空对象字面量({})是不同的,因为它们的引用地址不同。

这种方法代码量极少,可读性也好,对于基本数据类型(数字、字符串、布尔值、null、undefined、NaN)去重非常高效和准确。但记住一点,它对对象的去重是基于引用地址的,两个内容完全一样的对象,如果内存地址不同,Set会把它们当作两个不同的元素。

除了Set,我们也可以利用数组自身的

filter()

方法结合

indexOf()

来去重。这个方法相对传统,理解起来也比较直观:遍历数组,如果当前元素是它第一次出现,就保留它。

const originalArray = [1, 2, 2, 'hello', 'world', 'hello', null, null, undefined, undefined, NaN, NaN];const uniqueArrayByFilter = originalArray.filter((item, index, self) => {  // indexOf会返回元素在数组中第一次出现的索引  // 如果当前元素的索引和它第一次出现的索引相同,说明它是第一次出现,就保留  return self.indexOf(item) === index;});console.log(uniqueArrayByFilter);// 结果:[1, 2, 'hello', 'world', null, undefined, NaN]// 注意:indexOf在处理NaN时,会有些“奇怪”,因为NaN !== NaN,所以indexOf(NaN)总是-1,可能导致多个NaN被保留。// 同样,它也无法直接处理对象去重。

这种方式的优点是兼容性好,在老旧浏览器环境里也能跑。但它的性能在数组元素较多时会明显下降,因为

indexOf

在每次迭代时都要从头开始搜索,导致时间复杂度上升。

再者,如果我们需要更精细的控制,或者要兼顾性能和顺序,

reduce()

方法结合

Map

或普通JavaScript对象来做查找表也是个不错的选择。

const originalArray = [1, 2, 2, 'hello', 'world', 'hello', null, null, undefined, undefined, NaN, NaN];const seenMap = new Map(); // 或者用一个普通对象 {}const uniqueArrayByReduce = originalArray.reduce((accumulator, currentItem) => {  // Map的key可以是任何类型,包括NaN,并且对NaN的判断是正确的  if (!seenMap.has(currentItem)) {    seenMap.set(currentItem, true);    accumulator.push(currentItem);  }  return accumulator;}, []);console.log(uniqueArrayByReduce);// 结果:[1, 2, 'hello', 'world', null, undefined, NaN]// 这个方法能正确处理NaN,并且保持了元素的原始顺序。

这种方法在性能上通常比

filter

+

indexOf

要好,因为它利用了哈希表的O(1)查找特性。同时,它也灵活,可以扩展去处理更复杂的去重逻辑,比如对象数组去重。

哪种JS数组去重方法更高效?性能考量与选择

谈到效率,这事儿就得具体分析了。在我看来,大多数时候,

Set

方法是你的首选。为什么呢?因为它底层实现通常是哈希表(或者说,是高度优化的数据结构),查找和插入的平均时间复杂度都是O(1)。这意味着,即使你的数组有几万甚至几十万个元素,它的处理速度也相当快,总的时间复杂度接近O(N)——也就是线性时间,数组多大,就大致花多少时间。

相比之下,

filter()

结合

indexOf()

的效率就没那么理想了。

indexOf()

在数组中查找元素,最坏情况下需要遍历整个数组。所以,在

filter

的每次迭代里,你又进行了一次潜在的O(N)操作,整个过程下来,时间复杂度就变成了O(N^2)。当数组规模小的时候,这点差异你可能感觉不到,但一旦数组达到几千甚至上万个元素,你就会明显感觉到页面卡顿,甚至浏览器崩溃。这种方法,我个人是不太推荐在生产环境中大规模使用的,除非你明确知道数组规模很小且不会增长。

至于

reduce()

结合

Map

(或者普通对象)的方案,它的性能表现和

Set

非常接近,平均时间复杂度也是O(N)。因为它同样利用了哈希表的快速查找能力。在某些老旧环境不支持Set,或者你需要更精细控制(比如你想在去重时对元素做一些额外处理)的场景下,这个方法就显得尤为有用。它能很好地平衡性能和灵活性。

所以,如果问我哪种最快,我通常会毫不犹豫地指向

Set

。如果兼容性是首要考量,或者你需要处理的对象类型比较特殊,

reduce

Map

会是更稳妥的选择。

如何处理对象数组去重?深入探讨复杂数据类型的挑战

处理对象数组去重,这可就不是简单地用Set或者

indexOf

能搞定的了。因为JavaScript在比较对象时,默认是比较它们的内存地址(引用),而不是它们的内容。所以,即使两个对象长得一模一样,比如

{id: 1, name: 'A'}

和另一个

{id: 1, name: 'A'}

,它们在内存中是两个独立的个体,Set会认为它们是不同的。

这时候,我们就需要定义一个“唯一性”的标准。通常,我们会基于对象的一个或多个属性来判断它们是否重复。比如,如果你的对象都有一个

id

属性,并且

id

是唯一的,那么我们就可以根据

id

来去重。

一个比较常见的做法是结合

filter()

Set

(或者

Map

)来记录已经“见过”的

id

const users = [  { id: 1, name: 'Alice', age: 30 },  { id: 2, name: 'Bob', age: 25 },  { id: 1, name: 'Alice', age: 30 }, // 重复项  { id: 3, name: 'Charlie', age: 35 },  { id: 2, name: 'Bob', age: 26 } // id重复,但age不同];const seenIds = new Set();const uniqueUsers = users.filter(user => {  // 如果这个id之前没见过  if (!seenIds.has(user.id)) {    seenIds.add(user.id); // 把它标记为已见过    return true; // 保留这个用户对象  }  return false; // 否则,丢弃});console.log(uniqueUsers);// 结果:// [//   { id: 1, name: 'Alice', age: 30 },//   { id: 2, name: 'Bob', age: 25 },//   { id: 3, name: 'Charlie', age: 35 }// ]

这种方式非常实用,因为它允许你自定义去重的逻辑。如果对象的唯一性需要由多个属性共同决定,比如

id

type

,你可以组合它们作为Set的key,例如

user.id + '-' + user.type

另一种稍微复杂但更通用的方法是利用

Map

来存储对象,以某个属性(或组合属性)作为key,对象本身作为value。这在需要保留“最新”或“最旧”的重复项时特别有用。

const users = [  { id: 1, name: 'Alice', age: 30 },  { id: 2, name: 'Bob', age: 25 },  { id: 1, name: 'Alice', age: 30 },  { id: 3, name: 'Charlie', age: 35 },  { id: 2, name: 'Bob', age: 26 } // id重复,但age不同,我们希望保留最新的(即这个)];const uniqueMap = new Map();users.forEach(user => {  // 以id作为key,每次遇到重复id,新的对象会覆盖旧的,从而保留最后一个  uniqueMap.set(user.id, user);});const uniqueUsersFromMap = Array.from(uniqueMap.values());// 或者 [...uniqueMap.values()]console.log(uniqueUsersFromMap);// 结果:// [//   { id: 1, name: 'Alice', age: 30 },//   { id: 2, name: 'Bob', age: 26 }, // 注意,这里保留了age为26的Bob//   { id: 3, name: 'Charlie', age: 35 }// ]

这种

Map

的方式,不仅能去重,还能灵活控制保留哪个重复项(比如最后一个出现的)。当然,如果你需要根据所有属性都完全一致才算重复,那么

JSON.stringify()

可以临时作为key,但要注意它的局限性:属性顺序不同会生成不同的字符串,且不能处理循环引用。

去重时如何保持原始顺序?兼顾效率与数据完整性

去重的时候,很多时候我们不仅要剔除重复项,还希望剩下的元素能保持它们在原始数组中的相对顺序。这其实是个挺重要的细节,因为数据的顺序有时会承载特定的业务含义。

幸运的是,我们前面提到的一些主流去重方法,在现代JavaScript环境中,大多都能很好地保持原始顺序。

Set

方法: 在ES6规范中,

Set

的迭代顺序并没有明确规定。但在实际的现代JavaScript引擎实现中(比如V8,也就是Chrome和Node.js用的),

Set

保持元素插入顺序的。所以,如果你用

[...new Set(arr)]

,结果数组的顺序会和原始数组中第一次出现的元素顺序一致。这对我来说,是Set能成为首选去重方法的重要原因之一,因为它同时兼顾了简洁、高效和顺序。

filter()

结合

indexOf()

这个方法是天然保持顺序的。它的逻辑就是“如果当前元素是它第一次出现,我就保留它”。自然而然地,保留下来的元素,它们的相对顺序和原始数组是完全一致的。所以,如果你对性能要求不是特别极致,或者数组规模不大,这个方法在保持顺序方面是完全可靠的。

reduce()

结合

Map

(或对象): 这种方式也能够完美地保持原始顺序。在

reduce

的迭代过程中,我们是按照原始数组的顺序一个接一个地处理元素的。当一个元素被判断为不重复并被

push

到累加器数组中时,它就是按照原始顺序被添加进去的。Map本身也保持了key的插入顺序,这进一步保证了最终结果的顺序性。

// 示例:使用reduce + Map 保持顺序const disorderedArr = ['apple', 'banana', 'apple', 'orange', 'banana', 'grape'];const uniqueAndOrdered = disorderedArr.reduce((acc, item) => {    if (!seenMap.has(item)) { // 假设seenMap已定义        seenMap.set(item, true);        acc.push(item);    }    return acc;}, []);console.log(uniqueAndOrdered); // ['apple', 'banana', 'orange', 'grape'] - 顺序完全保留

所以,你不需要为了保持顺序而额外做太多工作。选择Set或者

reduce

+

Map

通常就能满足你的需求,它们在保持顺序的同时,也提供了不错的性能。只有在极少数需要兼容非常老旧的Set实现,或者对

filter

+

indexOf

的O(N^2)性能有严格限制时,你才需要特别去权衡。但就我个人经验而言,现代开发中,Set的方案几乎是万金油。

以上就是js如何实现数组去重的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1515151.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
js 怎么实现文件上传
上一篇 2025年12月20日 08:54:47
js中如何生成uuid
下一篇 2025年12月20日 08:55:11

相关推荐

  • 减少PHP与MySQL数据库通信的延迟

    减少php与mysql数据库通信的延迟可以通过以下策略:1. 优化数据库查询,使用索引提升查询速度;2. 减少数据库连接次数,使用连接池管理连接;3. 查询优化,使用explain分析查询计划;4. 使用缓存,如redis,减少数据库查询次数。这些方法能显著提升应用性能,但需权衡利弊,确保系统稳定性…

    2026年9月24日
    000
  • 讯维解决KVM鼠标不同步

    讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步讯维解决KVM鼠标不同步

    使用网络kvm时,常遇到本地鼠标与远程界面光标位置不一致的问题,即鼠标不同步现象,严重影响操作流畅性。可通过优化鼠标同步设置、更新驱动程序或选用兼容性更强的设备来有效改善。 1、配置运行Windows 2000操作系统的服务器环境 2、调整鼠标相关参数 3、点击开始菜单,进入控制面板,选择“鼠标”进…

    2026年9月24日 用户投稿
    900
  • 三星手机微信收款语音播报怎么开启?详细教程助你设置成功

    要让三星手机微信收款语音播报正常工作,需先检查微信内“收款到账语音提醒”是否开启,再确保手机系统中微信的通知权限完整开启、电池优化设为“不受限制”,同时确认媒体音量未静音、勿扰模式未启用;此外,定期清理缓存、保持应用与系统更新、避免第三方清理软件误杀后台,可保障通知长期稳定。 三星手机要开启微信收款…

    2026年9月24日
    300
  • 俄罗斯搜索引擎入口 俄罗斯Yandex浏览器官网在线进入

    俄罗斯搜索引擎Yandex的官网入口是https://yandex.com/,该平台提供多语言搜索、地图、新闻聚合和翻译工具,其浏览器以轻量、快速、广告过滤和高兼容性为优势,搜索支持多类型内容精准查找与安全防护。 俄罗斯搜索引擎入口在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来俄罗斯Ya…

    2026年9月24日
    200
  • 抖音水印怎么去掉?抖音水印在哪里关闭

    随着抖音的广泛使用,越来越多的人选择在这个平台上分享生活点滴。然而,在保存或转发视频时,常常会遇到水印问题,这不仅影响了视频的整体观感,也可能带来隐私风险。本文将为您详细讲解几种去除抖音视频水印的方法,帮助您轻松还原视频原本面貌。 一、常见的去水印方式 借助第三方工具软件 目前市面上有不少专门用于去…

    2026年9月24日
    600
  • 如何分析Linux进程内存 pmap内存映射检查方法

    如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法如何分析Linux进程内存 pmap内存映射检查方法

    要分析linux进程的内存,特别是利用pmap工具,核心操作是获取目标进程pid后执行pmap -x 。1. 获取pid可通过ps aux | grep your_process_name;2. 执行pmap -x 命令查看扩展格式信息,包括address、kbytes、rss、dirty、mode…

    2026年9月24日 用户投稿
    200
  • PHP实时输出如何防止XSS攻击_PHP实时输出安全防范XSS攻击

    防止XSS攻击需坚持三重防护:首先对用户输入进行严格验证与白名单过滤,使用filter_var等函数校验数据格式;其次根据输出上下文进行恰当转义——HTML正文和属性用htmlspecialchars(),JavaScript变量用json_encode(),URL参数用urlencode();最后…

    2026年9月24日
    100
  • UC浏览器历史记录最多能保存多久_UC浏览器历史记录保存时长说明

    UC浏览器本地历史记录默认保留约30天,超出后自动清理;通过开启云同步功能可长期保存并跨设备恢复;重要记录建议手动导出、截图或收藏以防丢失。 如果您发现UC浏览器中的历史记录无法找到,或者想了解其保存机制以决定是否需要手动备份,这通常与浏览器的自动清理策略和用户设置有关。以下是关于UC浏览器历史记录…

    2026年9月24日
    000
  • VSCode如何优化多语言混编 VSCode复合工程项目的管理技巧

    #%#$#%@%@%$#%$#%#%#$%@_e2fc++805085e25c9761616c00e065bfe8处理多语言混编和复杂项目的核心策略是使用多根工作区(multi-root workspace),通过创建.code-workspace文件将不同语言或模块的目录统一管理,实现跨项目文件浏…

    2026年9月24日
    000
  • 处理PHP多线程的定时任务并行_优化php多线程怎么实现的定时任务执行

    PHP可通过多进程、消息队列等方式实现定时任务并行处理。1. 使用pthreads扩展(需ZTS支持)可在CLI环境实现多线程,但部署复杂;2. 利用pcntl_fork创建子进程是推荐方案,通过fork多个进程并行执行任务,适合CLI模式;3. 通过crontab同时触发多个独立脚本或使用exec…

    2026年9月24日
    200
  • 怎样处理C++中的野指针问题 空指针检测与防御性编程

    怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程怎样处理C++中的野指针问题 空指针检测与防御性编程

    野指针难以发现是因为其指向已失效或非法内存,解引用会导致未定义行为。1. 初始化是关键防线,声明指针时必须赋初值或设为nullptr;2. 使用智能指针std::unique_ptr和std::shared_ptr可自动管理内存生命周期,避免手动delete遗漏;3. 防御性编程要求每次使用指针前进…

    2026年9月24日 用户投稿
    200
  • 360浏览器怎么关闭网页预加载_360浏览器禁用后台预加载提升性能设置

    关闭360浏览器预加载功能可减少资源占用,依次通过设置中心关闭网页预加载、禁用加速功能、修改隐私与安全设置限制后台行为。 如果您发现360浏览器在后台自动预加载网页,导致系统资源占用较高或网络变慢,可能是由于浏览器的智能预加载功能正在运行。该功能会提前加载您可能访问的网页内容以提升浏览速度,但同时也…

    2026年9月24日
    100
  • VSCode如何实现移动端调试 VSCode连接Android/iOS设备的技巧

    vscode本身不支持移动端调试,但可通过插件和工具间接实现。1. 调试android应用时,需开启设备开发者模式和usb调试,连接电脑后通过chrome浏览器访问chrome://inspect/#devices,使用chrome devtools调试webview;可配合vscode的debug…

    2026年9月24日
    000
  • iPhoneXSMax为什么收款语音不响?教你快速设置微信语音功能

    iPhoneXSMax为什么收款语音不响?教你快速设置微信语音功能iPhoneXSMax为什么收款语音不响?教你快速设置微信语音功能iPhoneXSMax为什么收款语音不响?教你快速设置微信语音功能iPhoneXSMax为什么收款语音不响?教你快速设置微信语音功能

    iPhone XS Max收款语音不响,通常由静音键、专注模式、通知权限或微信内部设置导致。首先确认物理静音键未开启,检查“专注模式”是否限制通知;进入系统“通知”设置,确保微信允许声音提醒;在微信App内开启“收款到账语音提醒”开关;同时确认后台刷新已启用,并排除低电量模式、蓝牙设备连接等干扰因素…

    2026年9月24日 用户投稿
    000
  • PHP 中如何将 JSON 数组值声明为变量

    本文介绍了如何在 PHP 中从数据库获取数据并将其编码为 JSON 格式,然后通过 AJAX 请求传递到另一个页面。重点讲解了如何在接收页面解析 JSON 数据,并将 JSON 数组中的特定值提取并赋值给变量,以便在后续的 PHP 函数中使用。 从数据库获取数据并编码为 JSON 首先,我们需要从数…

    2026年9月24日
    000
  • safari浏览器怎么快速搜索当前网站内容_safari浏览器网站内快速搜索方法

    1、使用Safari内置查找功能可快速定位网页关键词,点击分享按钮后选择“在页面上查找”并输入内容即可高亮显示结果;2、通过地址栏输入“site:域名 关键词”实现站内搜索,精准获取当前网站信息;3、创建含搜索语法的书签(如https://en.wikipedia.org/wiki/Special:…

    2026年9月24日
    000
  • 如何列出DEB包内容 dpkg -L查看文件清单

    如何列出DEB包内容 dpkg -L查看文件清单如何列出DEB包内容 dpkg -L查看文件清单如何列出DEB包内容 dpkg -L查看文件清单如何列出DEB包内容 dpkg -L查看文件清单

    要查看已安装 deb 包所包含的文件列表,可使用命令 dpkg -l 包名,例如 dpkg -l nginx 会列出 nginx 安装的所有文件路径;该命令适用于 debian 及其衍生系统如 ubuntu,仅能查询已安装的包,且常用于查找配置文件、排查冲突或学习软件结构;为方便查看,可通过管道配合…

    2026年9月24日 用户投稿
    000
  • VSCode如何调试React前端应用 VSCode调试React组件的完整教程

    要调试react前端应用,首先需安装vscode的浏览器调试插件并配置launch.json文件,1. 安装“debugger for chrome”或对应浏览器的插件;2. 在项目根目录的.vscode文件夹中创建launch.json,配置type为chrome、request为launch、n…

    2026年9月24日
    100
  • 360浏览器怎么升级到最新版本 360浏览器版本更新升级操作指南

    建议及时升级360浏览器至最新版本以确保安全与性能,可通过浏览器内置更新、官网手动下载或应用商店三种方式完成升级操作。 如果您发现当前使用的360浏览器功能受限或存在兼容性问题,可能是由于版本过旧导致。为确保浏览安全与性能稳定,建议及时将浏览器升级至最新版本。 本文运行环境:华为Mate 60 Pr…

    2026年9月24日
    100
  • iPhone13ProMax微信收款语音无法设置怎么办?解决语音功能的教程

    iPhone13ProMax微信收款语音无法设置怎么办?解决语音功能的教程iPhone13ProMax微信收款语音无法设置怎么办?解决语音功能的教程iPhone13ProMax微信收款语音无法设置怎么办?解决语音功能的教程iPhone13ProMax微信收款语音无法设置怎么办?解决语音功能的教程

    iPhone 13 Pro Max微信收款语音无法设置,通常非硬件问题,而是微信或系统设置不当所致。2. 需检查微信内“收款到账语音提醒”是否开启,并确认系统通知权限、声音设置、静音模式、勿扰模式及网络连接正常。3. 可尝试重启手机、更新微信或iOS系统,必要时重置所有设置或重装微信。4. 若问题依…

    2026年9月24日 用户投稿
    200

发表回复

登录后才能评论
关注微信