ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 Set 过滤数组中的唯一元素:JavaScript 数组去重的 Map/Set 实战解析

使用 Set 过滤数组中的唯一元素:JavaScript 数组去重的 Map/Set 实战解析 文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载导读本文围绕《现代 JavaScript 教程》中 过滤数组中的唯一元素unique(arr)这一核心练习展开系统讲解如何借助Set集合类型完成数组去重。你将掌握Set的完整 API 与迭代特性、Array.from与展开运算符两种官方推荐解法、基于filter/indexOf的传统实现及其性能差异并通过仓库内的源码与测试用例验证每种方案的边界行为包括NaN、对象引用与空数组。读完本文你不仅能独立写出健壮的去重函数还能理解Set与Map在唯一性判断上的底层比较算法为后续的实战编码打下基础。任务概述实现unique(arr)在 任务描述 中要求实现一个函数unique(arr)给定一个数组arr返回一个由其中所有唯一元素组成的新数组。function unique(arr) { /* 你的代码 */ } let values [Hare, Krishna, Hare, Krishna, Krishna, Krishna, Hare, Hare, :-O ]; alert( unique(values) ); // Hare, Krishna, :-O任务给出了两点关键提示元素类型不限于字符串示例中虽然用的是string类型但唯一性过滤对任何类型的值都应成立包括数字、布尔值、对象引用甚至NaN。使用Set来存储唯一值这正是本任务想训练的核心——Set天然保证每个值只出现一次。前置知识Set是什么Set是 ES6 引入的特殊类型集合即值的集合没有键它的每一个值只能出现一次。完整的 API 在 Map and Set 章节 中有系统讲解核心成员如下new Set(iterable)—— 创建一个set如果传入一个可迭代对象通常是数组会把其中的值复制进set。set.add(value)—— 添加一个值返回set本身可链式调用。set.delete(value)—— 删除值如果value在调用时存在返回true否则返回false。set.has(value)—— 如果value在set中返回true否则返回false。set.clear()—— 清空set。set.size—— 返回元素个数注意是属性不是方法。Set最重要的特性是重复使用同一个值调用set.add(value)不会产生任何变化。这一点恰好就是去重问题的核心诉求——把数组里的元素逐个add进Set重复值会被自动忽略。Set与数组 find方案的性能差异Set的替代方案可以是一个普通数组每次插入时用arr.find检查是否已存在。但正如 Map and Set 章节 所指出的这种方案性能很差因为find需要遍历整个数组来检查每个元素时间复杂度为 O(n²)。而Set内部对唯一性检查做了优化整体复杂度为 O(n)。与Map的对比键值对 vs 唯一值Set与Map是一对姊妹数据结构Map是带键的数据项集合允许任何类型的键包括对象并提供map.set(key, value)、map.get(key)、map.has(key)、map.delete(key)、map.clear()与map.size等完整 APISet是唯一值的集合没有键只关心值是否重复。两者在迭代上也完全兼容Set同样支持set.keys()、set.values()与set.entries()后两者是为兼容Map而存在entries()返回[value, value]且迭代顺序都遵循插入顺序。这也解释了为什么在去重场景中Set比Map更简洁——我们不需要把元素当作键来存储只需收集唯一值即可。官方解法Array.from(new Set(arr))仓库中 array-unique-map 任务的参考实现 给出了最简洁、最符合任务意图的解法function unique(arr) { return Array.from(new Set(arr)); }这短短一行完成了两步工作new Set(arr)利用Set的可迭代构造器将数组中的元素逐个加入集合重复值自动被忽略Array.from(new Set(...))由于Set本身是可迭代对象iterable而非数组需要用Array.from把它转换为数组返回。Array.from在这里不是可有可无的——它能把任何可迭代对象或类数组对象转换为真正的数组。这一点在 迭代键任务 中有更直接的体现map.keys()返回的是可迭代对象而非数组直接调用keys.push(more)会抛出Error: keys.push is not a function必须先Array.from(map.keys())再操作。let map new Map(); map.set(name, John); let keys Array.from(map.keys()); // 关键转换 keys.push(more); alert(keys); // name, more同理Set也支持for..of与forEach迭代但若需要数组方法如push、map、filter同样要先经过Array.from。多种可行的去重实现对比除了官方解法该问题在工程实践中还有多种常见写法各有适用场景。以下实现均可在浏览器控制台或 Node.js 中直接运行验证。解法一Array.from(new Set(arr))推荐function unique(arr) { return Array.from(new Set(arr)); }优点代码极简、语义清晰、性能 O(n)、适用于任何类型的值含NaN与对象引用。注意Set基于SameValueZero算法比较值相等性——它和严格等于差不多唯一区别是NaN被视为等于NaN。因此[NaN, NaN]去重后会得到[NaN]而不会像某些基于的实现那样保留两个NaN。解法二展开运算符[...new Set(arr)]function unique(arr) { return [...new Set(arr)]; }由于Set是可迭代对象展开运算符同样能将其摊开进数组字面量。这与Array.from效果等价且写法更短。二者的取舍在于Array.from更显式、可读性更好且对类数组对象如arguments、NodeList同样有效展开运算符只适用于可迭代对象遇到类数组对象会失败。解法三filterindexOf经典但较慢function unique(arr) { return arr.filter((item, index) arr.indexOf(item) index); }原理indexOf返回元素第一次出现的位置只有当当前下标等于首次出现下标时该元素才是首次亮相从而保留下来。局限indexOf使用严格相等比较无法正确处理NaN——[NaN, NaN]会得到[NaN, NaN]两个都保留因为indexOf(NaN)恒为-1。此外时间复杂度为 O(n²)大数组下性能较差。解法四forEachincludes可读性好function unique(arr) { let result []; arr.forEach(item { if (!result.includes(item)) { result.push(item); } }); return result; }原理直观遍历原数组只有当前值不在结果数组中时才推入。与indexOf类似includes同样基于SameValueZero算法能识别NaN但整体复杂度仍为 O(n²)。各方案行为对照表方案写法复杂度能否处理NaN返回新数组不改原数组Set Array.fromArray.from(new Set(arr))O(n)✅SameValueZero✅Set 展开运算符[...new Set(arr)]O(n)✅SameValueZero✅filter indexOfarr.filter((v, i) arr.indexOf(v) i)O(n²)❌比较✅forEach includes手动收集O(n²)✅SameValueZero✅在 任务描述 的 P.S. 中明确建议使用Set来存储唯一值因此前两种方案更契合本任务的训练意图。源码佐证测试用例如何验证正确性仓库中的 test.js 用 Mocha 框架从两个维度约束了unique的行为任何合格的实现都必须通过describe(unique, function() { it(removes non-unique elements, function() { let strings [Hare, Krishna, Hare, Krishna, Krishna, Krishna, Hare, Hare, :-O ]; assert.deepEqual(unique(strings), [Hare, Krishna, :-O]); }); it(does not change the source array, function() { let strings [Krishna, Krishna, Hare, Hare]; unique(strings); assert.deepEqual(strings, [Krishna, Krishna, Hare, Hare]); }); });这两个用例揭示了去重函数必须满足的两条不变量结果正确性重复元素必须被移除且保留顺序与首次出现顺序一致Set的插入顺序迭代天然保证这一点无副作用unique不能修改原数组——Set构造函数只是读取并复制传入的可迭代对象原数组保持不变。这一点也是Array.from(new Set(arr))优于某些原地修改写法的关键。深度延伸Set/Map在同类问题中的实战应用用集合做唯一性判定的思路在本仓库中还有两个同系列练习可以作为本任务的进阶延伸。延伸一用Map过滤字谜anagrams在 过滤字谜任务 中要求清除互为字谜的单词相同字母、不同顺序如nap与PAN。参考实现见 solution.js的核心思路是把每个单词拆成字母、排序后再拼接作为Map的键从而让所有字谜映射到同一把键function aclean(arr) { let map new Map(); for (let word of arr) { // 将单词 split 成字母排序再 join 回来 let sorted word.toLowerCase().split().sort().join(); map.set(sorted, word); // 同键覆盖每组字谜只保留最后遇到的那个 } return Array.from(map.values()); } let arr [nap, teachers, cheaters, PAN, ear, era, hectares]; alert( aclean(arr) ); // nap,teachers,ear或 PAN,cheaters,era这里map.set(sorted, word)的覆盖语义恰好充当了去重机制同一键再次写入时旧值被新值替换每组字谜最终只保留一个单词最后Array.from(map.values())把值迭代为数组返回——这与本任务Array.from(new Set(arr))的收尾方式如出一辙。测试用例test.js还额外验证了大小写不敏感era与EAR视为同一字谜以及每组字谜恰好保留一个。延伸二迭代对象与Array.from的转换在 迭代键任务 中map.keys()返回的是可迭代对象而不是数组直接调用数组方法会报错。这提醒我们凡是把Set/Map的迭代结果当作数组使用push、map、filter、sort等之前都必须先用Array.from或展开运算符完成转换。这也是官方解法中选择Array.from(new Set(arr))而非直接返回Set的根本原因。总结与自检清单本任务以一行Array.from(new Set(arr))收尾但背后承载的知识点值得完整沉淀Set天然去重重复add同一值不会改变集合set.size即唯一元素个数Array.from的必要性Set是可迭代对象而非数组返回前必须显式转换SameValueZero比较算法Set用该算法判重NaN被视为等于自身且算法不可自定义无副作用原则unique应返回新数组不修改原数组测试用例明确约束了这一点复杂度意识Set方案 O(n)filter indexOf方案 O(n²)大数据量下差距显著。动手验证时可直接在浏览器 DevTools 或 Node.js 中粘贴本节任一实现并对照 test.js 的两条断言逐项检查进阶读者可继续完成 过滤字谜 与 迭代键 两个兄弟任务把Map/Set的键控去重与迭代转换能力一并打通。赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐JavaScript 数组去重从 includes 朴素解法到 Set 优化zh.javascript.info 实战解析JavaScript 数组去重从 includes 朴素解法到 Set 优化zh.javascript.info 实战解析 本篇文章围绕《现代 JavaS文档教程前端JavaScript 数组去重从 includes 朴素解法到 Set 优化——Modern JavaScript Tutorial 实战任务解析JavaScript 数组去重从 includes 朴素解法到 Set 优化——Modern JavaScript Tutorial 实战任务解析 数组去重是文档教程前端30 seconds of code使用 Set 与数组方法去除 JavaScript 数组重复值30 seconds of code使用 Set 与数组方法去除 JavaScript 数组重复值 本文聚焦 30 seconds of code 仓库中的《教程文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表