搞不定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)是解决问题的关键。在实际开发中,应根据需求选择最适合的方法,并通过测试确保逻辑正确性。

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

相关推荐
豌豆学姐1 小时前
likeadmin-api 全驱动数字人参数避坑:file_url、ref_file_url 和 mode 怎么传
人工智能·aigc·api·数字人·全驱动数字人
海兰1 小时前
mcporter — 安装部署及使用完全指南(四)
人工智能·agent·openclaw
手写码匠1 小时前
华为云Flexus+DeepSeek征文|Dify 多 Agent 评测实战:用 DeepSeek-R1 当裁判,打造多智能体系统的自动化质量保障体系
人工智能·深度学习·算法·aigc
MartinYeung51 小时前
[论文学习]JBShield:通过激活概念分析与操纵防御大语言模型越狱攻击
人工智能·学习·语言模型
ZJU_统一阿萨姆1 小时前
【推理优化】KV Cache 量化:在不牺牲质量的前提下压榨更多内存
人工智能·语言模型·系统架构·vllm
三8441 小时前
CSRF跨站请求伪造基础
前端·csrf
安逸sgr1 小时前
Dropout 和正则化:深度学习如何缓解过拟合?
人工智能·ai·大模型·agent·智能体
weixin_446260851 小时前
Intern-S2-Preview:面向科学智能的智能体基础大模型
人工智能
汽车仪器仪表相关领域1 小时前
SIRIUS R1DB/R2DB便携式一体化数据采集系统
大数据·人工智能·功能测试·深度学习·压力测试