搞不定JavaScript的数组去重?你可能漏了这两个坑

  • 搞不定JavaScript的数组去重?你可能漏了这两个坑*

引言

数组去重是JavaScript开发中的常见需求,看似简单,却暗藏玄机。许多开发者认为用Setfilter就能轻松搞定,但实际场景中可能会遇到意想不到的"坑"。本文将从底层原理出发,深入剖析两个容易被忽略的数组去重问题:引用类型的去重特殊值的去重。通过对比不同方法的优劣,帮助你写出更健壮的代码。

主体

1. 基本方法回顾:为什么Setfilter有时会失效?

最常见的数组去重方法是使用Setfilter结合indexOf

javascript 复制代码
// 方法1:Set
const uniqueArray = [...new Set(array)];

// 方法2:filter + indexOf
const uniqueArray = array.filter((item, index) => array.indexOf(item) === index);

这两种方法对基本类型(如numberstringboolean)表现良好,但在以下场景中会失效:

  • 引用类型去重SetindexOf通过严格相等(===)比较,两个内容相同的对象会被视为不同。
  • 特殊值去重NaNnullundefined等值在去重时可能表现异常。

2. 第一个坑:引用类型的去重

假设数组包含对象或数组:

javascript 复制代码
const arr = [{ a: 1 }, { a: 1 }, [1, 2], [1, 2]];
const uniqueArr = [...new Set(arr)]; // 无效!

此时,Set无法去重,因为{ a: 1 } === { a: 1 }false

解决方案:深度比较或序列化
  • 深度比较 :使用lodashisEqual或自定义递归比较函数,但性能较差。
  • 序列化:将对象转为JSON字符串去重,再转回对象:
javascript 复制代码
const uniqueArr = Array.from(
  new Set(arr.map(item => JSON.stringify(item)))
).map(item => JSON.parse(item));
  • 局限性*:
  • 对象属性顺序变化会导致误判({a:1, b:2}{b:2, a:1}被视为不同)。
  • 无法处理包含循环引用的对象。

3. 第二个坑:特殊值的去重

JavaScript中某些值的比较行为特殊:

  • NaNNaN === NaNfalse,导致SetindexOf无法去重。
  • +0-0+0 === -0true,但某些场景需要区分。
  • nullundefined:需明确是否需要区分。
解决方案:自定义比较函数

针对NaN

javascript 复制代码
const arr = [1, NaN, 2, NaN];
const uniqueArr = arr.filter((item, index) => {
  return arr.findIndex(i => (Number.isNaN(i) && Number.isNaN(item)) || i === item) === index;
});

针对+0-0

javascript 复制代码
const arr = [+0, -0, 0];
const uniqueArr = [...new Set(arr)]; // 无法区分
const uniqueArr = arr.filter((item, index) => {
  return arr.findIndex(i => Object.is(i, item)) === index; // 区分+0和-0
});

4. 性能与兼容性权衡

方法 优点 缺点
Set 简洁、高效 不兼容IE11,无法处理引用类型
filter + indexOf 兼容性好 时间复杂度O(n²),性能差
序列化 支持引用类型 性能低,有局限性
自定义比较 灵活、精准 实现复杂
  • 现代浏览器 :优先使用Set
  • 兼容性要求高 :结合filterfindIndex
  • 复杂场景 :使用lodashuniqByuniqWith

5. 最佳实践

  • 明确需求 :是否需要区分+0/-0?是否需要深度比较对象?
  • 性能优化:大数据量时避免递归或嵌套循环。
  • 测试覆盖 :针对NaNnull、引用类型编写单元测试。

总结

数组去重并非"一招鲜"问题。Setfilter虽简单,但无法覆盖引用类型和特殊值的场景。理解JavaScript的相等比较机制(===Object.isJSON.stringify)是解决问题的关键。在实际开发中,应根据需求选择最适合的方法,并通过测试确保逻辑正确性。

希望本文能帮助你避开这两个"坑",写出更健壮的数组去重代码!

相关推荐
FII工业富联科技服务5 小时前
Omniverse + Isaac Teleop + 合成数据:工业富联机器人大脑训练+执行落地闭环拆解
大数据·人工智能·深度学习·机器学习·机器人·制造·具身智能
JeffongTan5 小时前
在LWC中镶嵌VF Page获取用户IP
前端·javascript·salesforce
东风破_6 小时前
大模型格式化输出
人工智能
Shockang6 小时前
AI 研究偏好模型
人工智能
ZGIAI6 小时前
律师最贵的不是知识,是时间:哪些工作真的可以先交给 Agent?
人工智能·架构
东风破_6 小时前
讲透 SSE:从流式响应到 LangChain model.stream()
人工智能
摇滚侠6 小时前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 使用 JdbcTemplate 阅读笔记 32
spring boot·笔记·后端
β添砖java7 小时前
深度学习31注意力机制、注意力分数、使用注意力机制的seq2seq、自注意力
人工智能·深度学习
ZGIAI7 小时前
销售团队最缺的不是另一个 AI,而是有人把跟进这件事一直做下去
人工智能·架构
隔窗听雨眠7 小时前
MCP会成为Agentic AI的标准吗?技术演进、生态博弈与标准之路的深度分析
人工智能