news 2026/9/16 11:41:04

es-toolkit 函数式编程指南:使用 fp/sampleSize 在 pipe 管道中随机采样

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
es-toolkit 函数式编程指南:使用 fp/sampleSize 在 pipe 管道中随机采样

es-toolkit 函数式编程指南:使用 fp/sampleSize 在 pipe 管道中随机采样

【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

sampleSize(函数式编程版)是 es-toolkitfp子模块提供的随机采样算子:它返回一个"等待数据"的函数,可从传入的数组中随机取出指定数量、且不重复数组位置的元素,专为与pipe组合使用而设计。读完本文,你将掌握fp/sampleSize的完整调用签名、边界行为与异常规则,并透过源码理解其基于 Floyd 算法与randomInt的底层实现,以及它与普通版sampleSize的差异和选型依据。

一、函数式变体与普通版的关系

在 es-toolkit 中,普通版sampleSize采用"数据优先"的调用方式(sampleSize(array, size)),而函数式编程版es-toolkit/fpsampleSize采用"数据置后"(data-last)的柯里化形态,用于在pipe中作为变换步骤使用。

形态调用方式适用场景
普通版sampleSize(array, size)普通代码中直接调用
fp 变体pipe(array, sampleSize(size))pipe组合多个变换步骤时

正如docs/fp/reference/sampleSize.md中的提示所强调:在普通代码中应优先使用原始版sampleSize,只有在用pipe组合变换时才使用 fp 变体。fp 变体并不重复实现采样逻辑,而是对普通版的一层薄封装(thin wrapper)。

二、基本用法

import { pipe, sampleSize } from 'es-toolkit/fp'; const values = pipe([1, 2, 3, 4], sampleSize(2)); // values 长度为 2,元素均来自输入数组

src/fp/array/sampleSize.ts的源码可以看到 fp 变体的实现非常简洁:它接收size作为配置参数,返回一个等待数组的函数,该函数在收到数组后转发给普通版实现:

export function sampleSize<T>(size: number): (array: readonly T[]) => T[] { return function (array: readonly T[]): T[] { return sampleSizeToolkit(array, size); }; }

这种"先传配置、后传数据"的模式正是es-toolkit/fp的通用设计:每个 fp 函数先接收配置(如map(fn)take(2)sampleSize(2)),返回一个接收数据的函数,再由pipe把上一步的输出作为下一步的输入依次贯穿。fp 变体同样通过src/fp/index.tssrc/fp/array/index.ts统一导出。

三、参数与返回值

参数

  • sizenumber):要从数组中随机取出的元素个数。

返回值

(array: readonly T[]) => T[]:一个将readonly T[]映射为随机元素数组的函数,即等待pipe喂入数据的变换步骤。

抛出异常

如果size大于传入数组的长度,会抛出错误。

// 抛出 Error: Size must be less than or equal to the length of array. pipe([1, 2, 3], sampleSize(4));

四、语义与边界行为

sampleSize返回size个来自输入数组的随机值,不会重复选择同一个数组位置(即无放回采样)。结合普通版文档docs/reference/array/sampleSize.md与测试用例,可以确认以下边界行为:

  • size为 0:返回空数组[],不会报错;
  • size等于数组长度:等价于洗牌(shuffle)效果,返回一个包含全部元素但顺序随机的新数组——注意它返回的是新数组,与原数组不是同一引用(result.not.toBe(array));
  • size大于数组长度:抛出Error,错误消息为'Size must be less than or equal to the length of array.'
  • 不修改原数组:函数返回新数组,输入数组保持不变。

这些行为在src/array/sampleSize.spec.ts中均有测试覆盖,例如:

it('returns the same array if the size is equal to the array length', () => { const array = [1, 2, 3]; const result = sampleSize(array, array.length); expect(result).toEqual(array); expect(result).not.toBe(array); });

fp 变体的测试src/fp/array/sampleSize.spec.ts则验证了在pipe中使用的效果:结果长度为size,且所有元素均来自输入数组。

五、底层实现原理:Floyd 算法

普通版sampleSize位于src/array/sampleSize.ts,其核心采用Floyd 算法(Robert Floyd 提出的无放回随机采样算法),在保证不重复选中的同时保持高效的线性复杂度:

export function sampleSize<T>(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; }

算法要点:

  1. 预分配结果数组const result = new Array(size)避免动态扩容;
  2. 去重集合const selected = new Set()记录已选中的下标;
  3. 增量随机范围:循环从array.length - size步进到array.length,每一步用randomInt(0, step + 1)生成一个半开区间[0, step + 1)内的随机整数下标;
  4. 冲突处理:若生成的下标已被选中,则退化为使用当前step下标,从而保证不重复、且一定能在size步内填满结果;
  5. 随机数来源randomInt(见src/math/randomInt.ts)基于random并向下取整,返回[minimum, maximum)之间的随机整数。

由于每一步都能在常数时间内完成(Sethas/add平均为 O(1)),整个采样过程的时间复杂度为 O(n),其中 n 为采样规模size

六、在 pipe 管道中组合使用

es-toolkit/fp的核心入口是pipe,其实现位于src/fp/pipe.tspipe将数据从左到右依次穿过每个函数,把嵌套调用f(g(h(x)))改写为自上而下的顺序步骤。sampleSize(size)返回的正是"等待数据"的一元函数,因此可以自然地插入管道中的任何位置,例如先过滤再采样:

import { pipe, filter, sampleSize } from 'es-toolkit/fp'; // 从 1~100 中先筛出偶数,再随机取 5 个 const result = pipe( Array.from({ length: 100 }, (_, i) => i + 1), filter(x => x % 2 === 0), sampleSize(5) ); // 结果长度为 5,元素均为偶数

需要说明的是:sampleSize本身是非惰性(eager)算子,它必须消费完整数组才能完成无放回采样,因此不会参与pipe的惰性融合(lazy fusion)优化路径;当pipe检测到连续惰性算子(如mapfiltertake)时才会融合为单趟遍历并支持提前终止(详见src/fp/pipe.ts中的chunkFunctionslazyPipe)。当sampleSize与其他惰性算子混用时,pipe会按"连续惰性算子组"分段处理,非惰性步骤按普通一元函数逐一执行,行为与直接调用一致。

七、典型应用场景

  • 问卷调查抽样:从候选题目或样本池中随机抽取指定数量的条目;
  • 游戏随机道具/对手:从物品列表或玩家列表中随机选出多个不重复元素;
  • 数据降采样:从大数据集中随机选取子集用于测试或演示;
  • 洗牌:令size等于数组长度,即得到一次完整的随机重排。

八、小结

  • fp/sampleSize是普通版sampleSize的 contenteditable="false">【免费下载链接】es-toolkitA modern JavaScript utility library that's 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 11:39:35

PHP性能优化实战:深入Zend引擎核心原理与调优技巧

做PHP开发这么多年&#xff0c;我一直觉得有个问题特别有意思&#xff1a;同一个项目&#xff0c;有的人服务器扛500并发就喘&#xff0c;换个懂行的人调一调&#xff0c;2000并发都稳如老狗。代码基本没动&#xff0c;差别在哪&#xff1f;大部分时候&#xff0c;差别就在你对…

作者头像 李华
网站建设 2026/9/16 11:39:04

VidBee:1000+ 网站视频离线下载指南

VidBee&#xff1a;1000 网站视频离线下载指南 【免费下载链接】VidBee Download video and audio from YouTube , TikTok , Twitter , Instagram , Facebook , Twitch , Bilibili , and 1000 sites—or import local media. Create searchable transcripts on your computer, …

作者头像 李华
网站建设 2026/9/16 11:38:30

Codex重构微信小游戏开发工作流:从需求到上线的语义协同

1. 这不是“用Codex写代码”&#xff0c;而是用Codex重构小游戏开发工作流“我用Codex做的微信小游戏&#xff0c;上线了”——这句话在技术圈刷屏时&#xff0c;我第一反应是&#xff1a;又一个标题党&#xff1f;点进去才发现&#xff0c;作者没吹牛&#xff0c;但也没说全。…

作者头像 李华
网站建设 2026/9/16 11:38:30

区块链投票系统实战:Solidity合约开发与Web3.js联调

简介&#xff1a;这是一份基于区块链的投票系统毕业设计源码与详细文档&#xff0c;面向计算机及相关专业学生&#xff0c;适用于毕业设计、期末大作业或课程设计。项目利用区块链不可篡改、公开透明的特性&#xff0c;设计投票流程与数据上链方案&#xff0c;涉及智能合约、椭…

作者头像 李华
网站建设 2026/9/16 11:38:22

中考备考策略与应试技巧全解析

1. 中考概述与核心定位中考&#xff08;Senior High School Entrance Examination&#xff09;是中国义务教育阶段最重要的分流考试之一&#xff0c;承担着初中教育质量评估和高中选拔录取的双重功能。作为连接九年义务教育和高中教育的枢纽环节&#xff0c;其考试结果直接影响…

作者头像 李华
网站建设 2026/9/16 11:36:17

CAD2012 网络许可和单机切换

由于公司使用的CAD2012 是网络租借许可的方式&#xff0c;如果许可有限&#xff0c;使用人数比许可数多&#xff0c;就会出现争抢问题。出现有些人得不到许可&#xff08;遇到公司不增加许可真的很坑B&#xff0c;因为A抢上了可能C被迫掉了&#xff0c;C上去O可能掉&#xff0c…

作者头像 李华