拓冰建站拓冰建站
首页 / 资讯中心 / 正文

es-toolkit 的 Map 版 keyBy:用键生成函数高效重组 Map 数据结构的实战指南

es-toolkit 的 Map 版 keyBy用键生成函数高效重组 Map 数据结构的实战指南【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkitkeyBy是 es-toolkit 在map模块中提供的一个实用函数给定一个Map和一个键生成函数getKeyFromEntry它会遍历原 Map 的每一个条目用该函数为每个条目生成新键最终返回一个新键 → 原值的全新Map。在按 ID、角色、分类等业务字段为 Map 数据建立索引、去重归组或重写键名等场景中它都能让你用一行代码完成原本需要手写循环的工作。读完本文你将掌握keyBy(map, getKeyFromEntry)的完整签名、参数与返回值语义、多个可直接运行的实操示例以及它的源码级实现原理与测试覆盖情况。功能概述一个函数重写整个 Map 的键keyBy解决的问题非常明确当你手头的数据以Map形式存储但现有键比如x、user1、item_1这类内部标识符并不适合后续查找而你希望基于每个条目本身的内容重新生成一套键时keyBy就是为此设计的。它的调用形式极其简洁const result keyBy(map, getKeyFromEntry);需要注意的一个关键设计是这个函数只能从es-toolkit/map子路径导入而不是从es-toolkit主入口导入。官方文档特别说明这是为了避免与其他集合类型如数组中的同名函数keyBy产生潜在冲突——实际上 es-toolkit 在 src/array/keyBy.ts 中确实提供了针对数组的keyBy二者行为相似但返回类型不同数组版返回普通对象RecordMap 版返回Map。因此在使用时请务必写清楚子路径import { keyBy } from es-toolkit/map;基本用法按值属性重新索引 Map文档中的第一个示例展示了最典型的应用场景把一个以单字符为键的 Map按照条目内部type字段重新组织。import { keyBy } from es-toolkit/map; const map new Map([ [x, { type: fruit, name: apple }], [y, { type: fruit, name: banana }], [z, { type: vegetable, name: carrot }], ]); const result keyBy(map, item item.type); // 结果: // Map(2) { // fruit { type: fruit, name: banana }, // vegetable { type: vegetable, name: carrot } // } // 注意: banana 被保留因为它是最后一个遇到的 fruit这里有两个要点值得强调新键来自值的内容原 Map 中x、y两个条目的值都属于fruit类型因此它们被归并到同一个新键fruit下Map的大小从 3 缩小为 2。同键冲突时后者胜出当多个条目生成了相同的新键时最后被遍历到的那个值会覆盖之前的值。这正是示例中banana取代apple的原因。多场景实践按各种业务标准重组数据keyBy的键生成函数非常灵活你可以基于任意规则来重组数据。下面是文档中给出的三组典型场景。按 ID 属性建立精确索引当值对象带有唯一标识如用户 ID时keyBy可以把它提升为 Map 的键让后续的map.get(101)查找变得直接高效import { keyBy } from es-toolkit/map; const users new Map([ [user1, { id: 101, name: Alice, role: admin }], [user2, { id: 102, name: Bob, role: user }], [user3, { id: 103, name: Charlie, role: user }], ]); const byId keyBy(users, user user.id); // 结果: 键为 101、102、103 的 Map按分类字段归组每组保留最后一个与精确索引不同当多个条目共享同一个分类值时keyBy会执行按分类归组、同组取最后一个的语义const byRole keyBy(users, user user.role); // 结果: Map(2) { // admin { id: 101, name: Alice, role: admin }, // user { id: 103, name: Charlie, role: user } // }可以看到user2和user3都属于user角色由于user3后出现最终user键下保留的是 Charlie。同时利用值和原键生成复合键键生成函数并非只能访问值它还能拿到原 Map 的键以及原 Map 本身因此可以构造出包含两者信息的复合键const inventory new Map([ [item_1, { category: electronics, price: 100 }], [item_2, { category: electronics, price: 200 }], ]); const categorized keyBy(inventory, (value, key) ${value.category}_${key}); // 结果: 键为 electronics_item_1、electronics_item_2 的 Map这种值 原键拼接的方式常用于需要保留原始标识信息的同时又按业务维度区分的场景。参数与返回值详解依据 keyBy 文档 与 源码定义完整的签名如下keyByK, V, K2( map: MapK, V, getKeyFromEntry: (value: V, key: K, object: MapK, V) K2 ): MapK2, V参数Parameters参数类型说明mapMapK, V要被重新映射的条目集合即原 Map 本身getKeyFromEntry(value: V, key: K, object: MapK, V) K2键生成函数接收值—键对并为每个条目生成新键其中getKeyFromEntry回调的三个入参分别是value: V当前条目的值key: K当前条目在原 Map中的键object: MapK, V原 Map 对象本身可在需要访问整体信息如map.size时使用。返回值ReturnsMapK2, V新生成的键映射到原条目值的新 Map。从类型签名可以看出三个泛型参数的职责K是原 Map 的键类型V是值类型K2是新键的类型。新键类型K2与原键类型K完全解耦这意味着新键可以是字符串、数字、Symbol甚至任意类型在 JS 中Map的键不像普通对象那样被限制为字符串或 Symbol这也正是 Map 版keyBy相比数组版返回Record更灵活的地方。源码级实现解析keyBy的实现非常精巧完整代码位于 src/map/keyBy.ts仅有十几行export function keyByK, V, K2( map: MapK, V, getKeyFromEntry: (value: V, key: K, object: MapK, V) K2 ): MapK2, V { const result new MapK2, V(); for (const [key, value] of map) { const newKey getKeyFromEntry(value, key, map); result.set(newKey, value); } return result; }其核心逻辑可以用四步概括新建空 Map创建MapK2, V类型的result作为返回容器遍历原 Map通过for...of迭代map的[key, value]条目对生成新键调用getKeyFromEntry(value, key, map)得到newKey这里就是文档中同时使用值和原键能力的来源——原 Map 的键和整个 Map 都被显式传入回调写入并返回执行result.set(newKey, value)遍历结束后返回result。从实现上可以直接观察出几个重要的行为特征最后一个值胜出的语义源自Map.set的覆盖行为result.set(newKey, value)对新键重复赋值时后写入的值天然覆盖先前的值无需任何额外的冲突判断代码不修改原 Map整个过程只向新建的result写入原map没有被删除或改写任何条目是纯粹的函数式操作这一点也有专门的测试用例保障见下文时间复杂度为 O(n)单次线性遍历没有嵌套循环或排序操作性能开销极小保持插入顺序由于Map本身维护插入顺序且result完全按原遍历顺序写入新 Map 中唯一键的顺序与原 Map 顺序一致。该函数从 src/map/index.ts 中对外导出与countBy、mapKeys、mapValues、findKey等 Map 工具函数一同构成 es-toolkit 的map模块。与相邻 Map 工具函数的对比在 es-toolkit 的map模块中keyBy并非孤军奋战理解它与兄弟函数的差异有助于选对工具keyByvsmapKeysmapKeys 的源码 同样接收(value, key, object)形式的转换函数但它只变换键、不改变值的归属逻辑且要求新键类型与原键类型一致泛型K不变返回的仍是同键类型的 Map而keyBy允许新键类型K2完全独立且天然具备同键取最后的归组能力。keyByvscountBycountBy 的源码 也按(value, key, object)生成分组键但返回值是MapK2, number即每个分组键对应的条目计数而不是条目本身keyBy保留的是值。二者一个用于数一数有多少一个用于按类存下代表值。keyByMap 版vskeyBy数组版数组版实现在 src/array/keyBy.ts接收数组并返回RecordK, T普通对象Map 版接收Map并返回MapK2, V且键类型不受PropertyKey字符串 | 数字 | Symbol限制。测试覆盖行为边界的全面验证keyBy的行为正确性在 src/map/keyBy.spec.ts 中有系统性的验证测试用例覆盖了文档中承诺的每一项语义非常适合作为理解函数边界的参考按值属性映射验证keyBy(map, item item.type)的归组结果同键取最后一个多个条目生成相同键时后遍历到的值胜出回调可访问原键(_value, key) \key_${key} 验证第二个参数确实传入原 Map 的键回调可访问原 Map通过expect(originalMap).toBe(map)严格断言第三个参数就是原 Map 对象本身且能读取originalMap.size空 Map 与单条目 Map空 Map 返回空Map()单条目正常返回不修改原 Map调用前后用Array.from(map.entries())对比确认原 Map 条目完全不变数字键验证值以数字开头作为新键时行为正确Symbol 键验证Symbol可以作为新键——这是普通对象Record无法直接等价做到的特性全部条目映射到同一键keyBy(map, () same)返回仅含一个条目的 Map且值取最后一项复杂对象作为值确认对象引用被原样保留不进行深拷贝唯一键保持插入顺序Array.from(result.keys())的结果与原顺序一致。这些用例不仅验证了功能正确性也从侧面印证了上文的实现推断纯新建容器、无副作用、线性遍历、顺序保留。适用前提与注意事项导入路径请务必从es-toolkit/map导入keyBy而非es-toolkit根入口否则可能与数组版keyBy产生命名冲突或无法解析键冲突策略keyBy的同键后者胜出是确定性的语义如果你的业务要求保留第一个匹配项需要在使用时自行反转数据顺序或改用其他工具返回新 MapkeyBy不会原地修改原 Map如果需要同时持有原数据无需担心副作用对象值保持引用返回的 Map 中的值直接引用原对象不会发生深拷贝修改返回值中的对象会影响原 Map 中的同一对象这一点与测试中复杂对象作为值的断言一致。总而言之keyBy是 es-toolkitmap模块中一个小而美的工具它的实现只有十几行却借助Map自身的特性优雅地完成了键重写、分类归组与索引重建等常见数据重组需求是处理键值型数据的日常得力助手。【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门