ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

es-toolkit 函数式编程:使用 fp 版 `sampleSize` 在 pipe 流水线中随机抽样

es-toolkit 函数式编程:使用 fp 版 `sampleSize` 在 pipe 流水线中随机抽样 es-toolkit 函数式编程使用 fp 版sampleSize在 pipe 流水线中随机抽样【免费下载链接】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-toolkites-toolkit 的fp子包将工具函数改造为柯里化形式使其能无缝接入pipe等函数组合工具。本篇指南以docs/ja/fp/reference/sampleSize.md为骨架讲解 fp 版sampleSize的用法、参数与错误行为并深入到src/fp/array/sampleSize.ts与src/array/sampleSize.ts的源码揭示其底层基于 Floyd 算法的无重复抽样原理。读完本文你将掌握如何在 es-toolkit 的函数式流水线中正确、高效地完成随机抽样并理解它与普通版sampleSize的取舍。一、fp 版sampleSize是什么fp 版sampleSize的作用是创建一个从数组中返回随机值的函数用于与函数式编程的pipe一起使用const result pipe(array, sampleSize(size));从调用形式上可以直观看出它与普通版的关键区别普通版sampleSize(array, size)一次性接收数组和数量两个参数fp 版sampleSize(size)先接收数量size返回一个等待数组输入的函数再由pipe把上游数据喂给它。import { pipe, sampleSize } from es-toolkit/fp; const values pipe([1, 2, 3, 4], sampleSize(2)); // values 的长度为 2元素来自输入数组从源码看这种柯里化只是薄薄一层包装。src/fp/array/sampleSize.ts的实现是export function sampleSizeT(size: number): (array: readonly T[]) T[] { return function (array: readonly T[]): T[] { return sampleSizeToolkit(array, size); }; }它直接委托给src/array/sampleSize.ts导出的普通版sampleSize源码中通过import { sampleSize as sampleSizeToolkit } from ../../array/sampleSize.ts引入因此 fp 版与普通版在抽样行为、返回的新数组以及抛错规则上完全一致区别仅在于参数的组织方式。二、与普通版sampleSize如何选择::: info在不使用管道组合的普通代码中推荐使用原版 es-toolkit 的sampleSize只有当你需要用pipe串联变换时才使用 fp 版。:::这一建议背后是清晰的工程逻辑普通代码里直接sampleSize(array, size)更直观、参数更少在pipe(array, fn1, fn2, ...)的流水线里每一步操作符都约定为接收一个值返回一个新函数fp 版sampleSize(size)恰好满足这一约定从而可以与filter、map、take等 fp 操作符自由组合。普通版sampleSize的完整用法可以参考docs/reference/array/sampleSize.md例如import { sampleSize } from es-toolkit/array; const numbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const randomNumbers sampleSize(numbers, 3); // 例如 [2, 7, 9]实际结果随机三、参数、返回值与错误行为参数size(number)要从管道传入的数组中返回的随机值个数。返回值(array: readonly T[]) T[]一个将readonly T[]映射为随机值数组的函数。也就是说sampleSize(size)的结果仍然是一个函数它等待pipe将上游数组传入后才真正执行抽样。错误当size大于管道传入数组的长度时会抛出错误。错误信息与普通版一致来自src/array/sampleSize.tsif (size array.length) { throw new Error(Size must be less than or equal to the length of array.); }对应的测试src/array/sampleSize.spec.ts验证了这一行为it(throws an error if the size is greater than the array length, () { expect(() sampleSize([1, 2, 3], 4)).toThrowErrorMatchingInlineSnapshot( [Error: Size must be less than or equal to the length of array.] ); });四、边界情况size 为 0 与 size 等于数组长度虽然 fp 版文档只规定了大于数组长度时抛错但深入源码与测试可以发现两个值得留意的边界行为size为 0 时返回空数组。测试src/array/sampleSize.spec.ts明确断言sampleSize([1, 2, 3], 0)的结果为[]。size等于数组长度时返回包含全部元素的新数组相当于洗牌效果但不会复用原数组引用。测试src/array/sampleSize.spec.ts同时断言result与array内容相等toEqual但引用不同not.toBe即返回的是新数组原数组不被修改。这两个特性同样适用于 fp 版因为 fp 版只是委托调用同一个底层实现。五、源码原理Floyd 算法与无重复抽样sampleSize的核心实现位于src/array/sampleSize.tsexport function sampleSizeT(array: readonly T[], size: number): T[] { if (size array.length) { throw new Error(Size must be less than or equal to the length of array.); } const result new Array(size); const selected new Set(); for (let step array.length - size, resultIndex 0; step array.length; step, resultIndex) { let index randomInt(0, step 1); if (selected.has(index)) { index step; } selected.add(index); result[resultIndex] array[index]; } return result; }从中可以提炼出几个关键实现事实采用 Floyd 算法源码注释明确标注其算法来源为 Robert Floyd 的经典抽样算法可以在只遍历一次、只调用size次随机数的前提下从n个元素中无重复地抽取k个元素时间复杂度为 O(k)空间上用一个Set记录已选下标。保证同一数组位置不重复每次抽样用selected.has(index)判断下标是否已被选中若已选中则回退为当前step位置这正是 Floyd 算法的精髓从而保证结果中不会出现重复的数组下标。随机数来源算法依赖src/math/randomInt.ts生成[0, step 1)区间内的随机整数而randomInt内部又调用src/math/random.ts基于Math.random()实现minimum Math.random() * (maximum - minimum)的区间映射。因此抽样结果的随机性最终来源于引擎的Math.random()。这也解释了 fp 版文档中不重复同一数组位置同じ配列位置は繰り返しません这一行为承诺的来源它不是约定俗成而是由 Floyd 算法的Set去重逻辑在源码层面保证的。六、在 pipe 流水线中的组合实战fp 版sampleSize的价值体现在长流水线中。下面是一个把从用户列表中随机抽取 3 名、再映射出姓名的完整组合示例import { pipe, sampleSize, map } from es-toolkit/fp; interface User { id: number; name: string; } const users: User[] [ { id: 1, name: Alice }, { id: 2, name: Bob }, { id: 3, name: Carol }, { id: 4, name: Dave }, { id: 5, name: Eve }, ]; // 从用户数组中随机抽取 3 人再取出姓名 const winners pipe(users, sampleSize(3), map(user user.name)); // winners: 例如 [Carol, Alice, Eve]实际结果随机pipe的从左到右数据流定义在src/fp/pipe.tssampleSize等 fp 操作符见src/fp/array/index.ts中的导出都遵循先收参数、再收数据的柯里化签名这正是它们能在pipe中直接作为操作符使用的原因。需要留意的限制是由于size在创建操作符时已经固定sampleSize(size)要求调用方事先知道数组的大致规模一旦传入的数组长度小于size流水线会在该步骤抛出错误错误信息与第五节所示一致。因此在流水线中使用时建议先通过filter等操作完成裁剪再根据裁剪后的规模选择合理的size或在外部对数组长度做前置校验。七、小结fp 版sampleSize(size)返回(array: readonly T[]) T[]形式的柯里化函数专为pipe流水线设计普通代码应优先使用sampleSize(array, size)二者底层实现完全相同抽样基于 Floyd 算法保证结果无重复下标、返回新数组且不修改原数组size array.length时抛错size 0返回空数组size array.length时相当于一次洗牌随机性来源是Math.random()经randomInt→random调用链适用于抽奖、问卷抽样、游戏随机选人等场景。【免费下载链接】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),仅供参考
返回列表