尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

es-toolkit invertBy 详解:键值反转与分组聚合的 Lodash 兼容实现

es-toolkit invertBy 详解:键值反转与分组聚合的 Lodash 兼容实现 es-toolkit invertBy 详解键值反转与分组聚合的 Lodash 兼容实现【免费下载链接】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-toolkitinvertBy是 es-toolkit 兼容层es-toolkit/compat中用于反转对象键值并按值分组的工具函数它将原对象的键与值互换并把映射到同一值或同一变换结果的所有原始键收集到一个数组中。本文以官方日文参考文档 docs/ja/compat/reference/object/invertBy.md 为主线结合 源码实现 与 测试用例讲解其使用方式、参数语义、底层原理以及与invert的区别和现代替代方案。读完本文你将掌握何时该用invertBy、如何通过iteratee灵活分组以及如何用原生 JavaScript 写出等价实现。功能概述键值反转 分组invertBy与普通invert见 src/compat/object/invert.ts最大的不同在于invert遇到重复值时只保留最后出现的键一对一反转而invertBy会把所有共享同一值的键收集成数组一对多分组。const inverted invertBy(object, iteratee);输入一个普通对象。输出新对象键为变换后的值值为原键组成的数组。官方文档给出的基础示例原样取自 docs/ja/compat/reference/object/invertBy.mdimport { invertBy } from es-toolkit/compat; // 基本的键值反转相同的值会被分组成数组 const object { a: 1, b: 2, c: 1 }; invertBy(object); // { 1: [a, c], 2: [b] } // 使用迭代器函数对值进行变换 const ages { john: 25, jane: 30, bob: 25 }; invertBy(ages, age age_${age}); // { age_25: [john, bob], age_30: [jane] } // 按字符串长度分组 const words { a: hello, b: world, c: hi, d: test }; invertBy(words, word word.length); // { 5: [a, b], 2: [c], 4: [d] }函数签名与参数说明invertBy(object, iteratee?)根据文档与源码签名src/compat/object/invertBy.ts函数接受两个参数参数类型说明objectRecordstring, T \| Recordnumber, T \| null \| undefined需要反转键值的对象iterateeValueIteratee可选对每个值进行变换的函数默认是恒等函数原样使用值作为键返回值Recordstring, string[]即变换后的值 → 原键数组的新对象。iteratee 的四种形式iteratee参数的类型ValueIteratee定义于 src/compat/_internal/ValueIteratee.tsexport type ValueIterateeT ((value: T) unknown) | (PropertyKey | [PropertyKey, any] | PartialShallowT);也就是说除了直接传函数还可以传字符串键名、[属性, 值]二元组或部分对象——它们都会经由 src/compat/util/iteratee.ts 的iteratee()工厂统一转换为判定函数见iteratee.ts中的switch分支函数原样返回直接以集合元素为参数调用属性名PropertyKey转换为取值函数property(value)等价于element element[key]属性-值二元组[key, value]转换为matchesProperty等价于element element[key] value部分对象PartialShallowT转换为matches返回元素是否匹配该部分对象的布尔值。文档特别演示了按对象属性分组这一常见用法import { invertBy } from es-toolkit/compat; // 按对象属性分组 const users { user1: { department: IT, age: 30 }, user2: { department: HR, age: 25 }, user3: { department: IT, age: 35 }, }; invertBy(users, user user.department); // { IT: [user1, user3], HR: [user2] }得益于ValueIteratee联合类型上例中的user user.department也可以写成属性名字符串department效果相同。空值安全处理invertBy对null和undefined完全安全。文档示例import { invertBy } from es-toolkit/compat; invertBy(null); // {} invertBy(undefined); // {}这一点在源码开头由isNil守卫保证src/predicate/isNil.ts 中isNil(x) (x null)并得到测试用例should return an empty object for nullish values的验证src/compat/object/invertBy.spec.ts。源码实现深度解析核心实现只有 20 余行src/compat/object/invertBy.ts可分为四步const result {} as Recordstring, string[]; if (isNil(object)) { return result; // 1. 空值短路直接返回空对象 } if (iteratee null) { iteratee identity as (value: T[keyof T]) string; // 2. 未传 iteratee 时回退为 identity } const keys Object.keys(object); const getString iterateeToolkit(iteratee); // 3. 将任意形式 iteratee 规范化为函数 for (let i 0; i keys.length; i) { const key keys[i] as string; const value (object as any)[key]; const valueStr getString(value); // 调用变换函数得到新键 if (Array.isArray(result[valueStr])) { result[valueStr].push(key); // 4a. 键已存在 → 追加到数组 } else { result[valueStr] [key]; // 4b. 键首次出现 → 初始化为单元素数组 } } return result;几个值得注意的实现细节默认 iteratee 是identity当iteratee为null/undefined时直接使用 src/function/identity.ts 中的恒等函数identity(x) x与测试用例should use identity when iteratee is nullish一致src/compat/object/invertBy.spec.ts。只遍历自有可枚举键Object.keys(object)只返回自有可枚举属性因此继承属性不会被纳入分组。测试用例should only add multiple values to own, not inherited, propertiessrc/compat/object/invertBy.spec.ts专门验证了这一点对{ a: hasOwnProperty, b: constructor }调用invertBy结果中的hasOwnProperty与constructor都是普通分组键不会被原型链上的同名方法干扰。分组采用惰性初始化数组首次遇到某键时创建[key]再次遇到时push避免了不必要的空数组预分配。与 invert 的对比选哪个es-toolkit 同时提供invert与invertBy二者区别如下场景invertinvertBy值唯一一对一反转新对象值为原键字符串同样可用但值为单元素数组值重复只保留最后一个键前值被覆盖收集所有键到数组不丢失信息自定义分组不支持 iteratee支持函数 / 属性名 / 二元组 / 部分对象典型用途反向查找表统计分组、按类目汇总键在 es-toolkit 中invert是核心库src/object/invert.ts的兼容层封装见 src/compat/object/invert.ts 中return invertToolkit(obj)而invertBy则在 compat 层独立实现。如果你需要值 → 键的反向映射且值不会重复优先用invert如果需要按值或按变换结果聚合多个键则用invertBy。性能注意官方建议与现代替代方案官方文档在开头明确给出警示由于复杂的迭代器处理和分组逻辑invertBy运行较慢建议在性能敏感场景改用更快的现代 JavaScript API——Object.entries()配合reduce()或Map。基于仓库实现遍历Object.keys 逐项调用iteratee 数组分组一个等价的现代写法如下// 等价于 invertBy(object) 的现代实现 const object { a: 1, b: 2, c: 1 }; const inverted Object.entries(object).reduceRecordstring, string[]((acc, [key, value]) { const groupKey String(value); (acc[groupKey] ?? []).push(key); return acc; }, {}); // { 1: [a, c], 2: [b] } // 需要自定义分组逻辑时只需替换 groupKey 的取值 // const groupKey String(user.department);需要 iteratee 变换时的等价实现const ages { john: 25, jane: 30, bob: 25 }; const result Object.entries(ages).reduceRecordstring, string[]((acc, [key, age]) { const groupKey age_${age}; (acc[groupKey] ?? []).push(key); return acc; }, {}); // { age_25: [john, bob], age_30: [jane] }在需要保持插入顺序、键为任意类型包括对象的分组场景Map是更合适的容器。不过在需要与 Lodash 行为完全兼容的迁移场景中例如从 lodash 的invertBy迁移直接使用es-toolkit/compat的invertBy依然是最省心的选择——两者的分组语义和返回值结构完全一致。测试验证行为契约一览仓库为invertBy提供了完整的 Vitest 测试src/compat/object/invertBy.spec.ts这些测试同时构成了该函数的行为契约iteratee 变换分组invertBy(object, value group value)得到{ group1: [a, c], group2: [b] }默认恒等行为不传或传null/undefined作为 iteratee 时结果一致值原样作为键自有属性限定值为hasOwnProperty、constructor等原型链上的名字时仍作为普通分组键处理不会触发原型污染空值安全invertBy(null)与invertBy(undefined)均返回{}。测试文件头部还注明了其用例源自 lodash 官方测试lodash/test/invertBy.spec.js这保证了es-toolkit/compat的invertBy与 Lodash 保持行为级兼容。小结invertBy是 es-toolkit 兼容层中一个小而精的分组反转工具默认按值原样分组、支持四种形式的iteratee、对空值安全、且严格限定自有属性遍历。其源码实现src/compat/object/invertBy.ts清晰展示了规范化 iteratee → 遍历键 → 惰性分组的标准套路可作为实现同类分组聚合逻辑的参考模板而在追求极致性能的场景官方文档建议改用Object.entries()reduce()或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),仅供参考
返回列表