重复数组:为什么这个问题在 Stack Overflow 上获得了数千次浏览
\n\n想象一下:您正在编写一个在线商店,用户将商品添加到购物车。但由于错误或 bug,同一个 iPhone 在列表中出现了三次。或者您正在解析数据库中的标签列表,而“JavaScript”连续出现了 15 次。该怎么办?没错——只保留唯一值。
\n\nStack Overflow 上的问题 “Get all unique values in a JavaScript array (remove duplicates)” 获得了数十万次浏览。这并非偶然。这个问题看似简单,但背后隐藏着一整套方法:从现代 ES6 的一行代码到使用 filter 和 indexOf 的巧妙技巧。在本文中,我们不仅会提供现成的解决方案,还会深入解析每种方法在 JavaScript 引擎层面的工作原理。
准备好一探究竟了吗?开始吧!
\n\n\n\n
方法 #1:Set —— 性能之王
\n\n获取 JavaScript 数组中的唯一值 最优雅且最快的方法是使用 Set 对象。它出现在 ES6 中,简直就是为此任务而生。
const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = [...new Set(numbers)];\n\nconsole.log(uniqueNumbers); // [1, 2, 3, 4, 5]\n\n底层工作原理是什么?
\nSet 对象只存储唯一值。当我们把数组传递给构造函数 new Set(numbers) 时,JavaScript 引擎会遍历每个元素并将其添加到内部的哈希表中。如果该值已存在,则会被忽略。然后,扩展运算符 ... 将 Set 转换回数组。
为什么它很快? 因为在 Set 中查找和插入的时间复杂度是 O(1)(常数时间)。对于包含 10,000 个元素的数组,与其他方法的性能差异将是巨大的。
\n\n重要提示: Set 会保留元素顺序,但仅适用于原始类型(数字、字符串、布尔值)。对于对象,比较是基于引用而非内容。
\n\n何时使用?
\n- \n
- 如果数组由数字或字符串组成。 \n
- 如果处理大型数组时速度很重要。 \n
- 如果您编写现代代码且不畏惧 ES6。 \n
\n\n
方法 #2:filter + indexOf —— 经典之作
\n\n这种方法是 JavaScript 中的老将。它无需 polyfill 即可在所有浏览器中运行,并且在 Stack Overflow 上仍然很受欢迎。
\n\nconst numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = numbers.filter((value, index, arr) => {\n return arr.indexOf(value) === index;\n});\n\nconsole.log(uniqueNumbers); // [1, 2, 3, 4, 5]\n\n逻辑解析
\nfilter 方法创建一个新数组,只保留回调函数返回 true 的元素。在回调函数内部,我们检查:当前索引是否是该值在数组中的第一次出现? 如果是,则保留该元素。如果该值之前出现过,则 indexOf 会返回一个更小的索引,条件不成立,重复项将被丢弃。
陷阱: indexOf 每次都会从头遍历数组。这导致时间复杂度为 O(n²)。对于小型数组(最多 1000 个元素),差异不明显,但在 100,000 个元素上,浏览器可能会卡顿一秒钟。
何时使用?
\n- \n
- 如果需要支持旧版浏览器(IE11)。 \n
- 如果数组很小。 \n
- 如果您想直观地展示“第一次出现”的逻辑。 \n
\n\n
方法 #3:reduce + 累加器 —— 灵活性与控制力
\n\nreduce 方法让您完全控制整个过程。我们可以自行决定如何收集结果。
const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = numbers.reduce((acc, value) => {\n if (!acc.includes(value)) {\n acc.push