ARTICLE DETAIL

资讯详情

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

es-toolkit/fp 函数式编程 `zip` 完全指南:在 pipe 管线中按索引合并多个数组

es-toolkit/fp 函数式编程 `zip` 完全指南:在 pipe 管线中按索引合并多个数组 es-toolkit/fp 函数式编程zip完全指南在 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 的函数式编程入口 es-toolkit/fp 提供了一系列数据最后传入data-last的操作符其中zip用于创建一个把被 pipe 传入的数组与预先配置好的数组按相同索引合并成元组的函数。本文以 docs/ja/fp/reference/zip.md 为核心结合 fp 版 zip 源码、普通版 zip 源码 与单元测试带你掌握它的调用方式、类型签名、边界行为长度不齐时以undefined补齐以及底层实现原理并理清它与pipe、zipObject、zipWith、unzip等兄弟函数的分工。一、fp版zip是什么在 es-toolkit/fp 中zip并不是直接接收数组并立刻返回结果的普通函数而是一个工厂函数你用待合并的数组配置项调用它得到一个等待数据的转换函数随后这个转换函数被放进pipe管线接收真正要被合并的那个数组。const result pipe(array, zip(...arrs));也就是说zip(...arrs)生成一个形如(array: readonly T[]) T[][]的函数pipe会把前一步的结果作为array传给它。何时使用fp版何时使用普通版官方文档给出了一条清晰的选择准则在不使用管线组合的普通代码中推荐使用 es-toolkit 主入口的zipimport { zip } from es-toolkit/array只有当你需要用pipe把多个转换串联起来时才使用本页的fp变体。两者的底层是同一套实现区别只在调用方式——fp版把待合并数组前置、把主数组后置从而契合管线的数据流方向。二、基本用法zip会把被 pipe 的数组与配置的数组在相同索引上的值合并成元组import { pipe, zip } from es-toolkit/fp; pipe([1, 2], zip([a, b])); // [[1, a], [2, b]] pipe([1, 2, 3], zip([a])); // [[1, a], [2, undefined], [3, undefined]]观察第二个例子当各数组长度不一致时结果的行数由最长的数组决定较短数组中缺失的位置用undefined填充。参数ParametersarrsArrayreadonly T[]要与被 pipe 的数组一起 zip 的数组可传零个、一个或多个。返回值Returns(array: readonly T[]) T[][]一个把被 pipe 的数组按索引转换为行元组的函数。与普通版zip的对比普通版直接传入所有数组一次性得到结果import { zip } from es-toolkit/array; zip([1, 2, 3], [a, b, c]); // Returns: [[1, a], [2, b], [3, c]] // 3 个数组长度不一致时同样以 undefined 补齐 zip([1, 2], [a, b, c], [true, false]); // Returns: [[1, a, true], [2, b, false], [undefined, c, undefined]]可以看到普通版zip与fp版的合并规则完全一致行数取最长数组、缺失补undefinedfp版只是把最后一个数组换成了后续由pipe喂入的数据。三、在pipe管线中的实战组合pipe是 es-toolkit/fp 的合成入口它把值从左到右依次穿过每个函数上一个函数的输出就是下一个函数的输入实现见 src/fp/pipe.ts。因此zip很适合放在管线末尾或中间把前一步产生的数组与常量数据按位置对齐import { map, pipe, zip } from es-toolkit/fp; const result pipe( [1, 2, 3], map(x x * 10), // [10, 20, 30] zip([十, 二十, 三十]) // [[十, 10], [二十, 20], [三十, 30]] );这里每一步都以数据待传入的函数形式存在pipe会按顺序把结果喂给下一步。即便zip不参与pipe的懒融合lazy fusion仅map、filter、take等连续出现的懒函数才会被融合为单次遍历管线也能正确地把上一步的完整数组交给zip生成的函数。四、源码级实现剖析1.fp包装层把参数前置、数据后置src/fp/array/zip.ts 是fp版的实现核心它做的事情非常简洁——用闭包捕获配置数组返回接收主数组的函数最终委托给普通版实现export function zip(...arrs: Arrayreadonly unknown[]): unknown { return function (array: readonly unknown[]): unknown[][] { return zipToolkit(array, ...arrs); }; }注意这里zipToolkit(array, ...arrs)的调用顺序被 pipe 传入的array是第一个参数而配置数组arrs依次排在后面与把配置写在调用处、把数据留给pipe的>export function zipT(...arrs: Arrayreadonly T[]): T[][] { // For performance reasons, use this implementation instead of // const rowCount Math.max(...arrs.map(x x.length)); let rowCount 0; for (let i 0; i arrs.length; i) { if (arrs[i].length rowCount) { rowCount arrs[i].length; } } const columnCount arrs.length; const result Array(rowCount); for (let i 0; i rowCount; i) { const row Array(columnCount); for (let j 0; j columnCount; j) { row[j] arrs[j][i]; } result[i] row; } return result; }源码注释明确解释了为什么不用Math.max(...arrs.map(x x.length))出于性能考虑改用一次for循环求最大值——避免先map出一个长度数组再展开Math.max的额外开销后者在大数组、多数组场景下还要承担展开参数的开销。当某个数组比rowCount短时arrs[j][i]自然取到undefined这正是缺失值补undefined这一语义的底层来源。3. 类型重载从 0 个到 N 个配置数组fp版zip的类型定义不是单一签名而是一组重载见 src/fp/array/zip.ts调用形式返回函数产生的元组类型典型示例zip()Array[T]单元素元组pipe([1, 2], zip())→[[1], [2]]zip(arr2)Array[T, U]pipe([1, 2], zip([a, b]))→[[1, a], [2, b]]zip(arr2, arr3)Array[T, U, V]合并 3 个数组zip(arr2, arr3, arr4)Array[T, U, V, W]合并 4 个数组zip(...arrs)T[][]任意数量数组的兜底签名正是这些重载让 TypeScript 能在你写pipe([1, 2], zip([a, b]))时推导出结果类型Array[number, string]而不是退化成宽泛的unknown[][]。调用zip()不传任何数组时返回的函数会把被 pipe 的每个元素包装成单元素元组pipe([1, 2], zip())得到[[1], [2]]。五、zip 家族zipObject、zipWith与unzipfp目录下还有几个与zip密切相关的函数理解它们能帮你更准确地选择 APIzipObject把被 pipe 的数组当作键与配置的值数组按索引配对构建对象。例如pipe([a, b] as const, zipObject([1, 2]))得到{ a: 1, b: 2 }src/fp/array/zipObject.ts。zipWithzip 之后再用一个合并函数combiner对每一组元组做变换例如pipe([1, 2], zipWith((value, index) value index))得到[1, 3]src/fp/array/zipWith.ts。unzipzip的逆操作把元组数组按元组位置拆回多个数组例如pipe([[1, a], [2, b]], unzip())得到[[1, 2], [a, b]]src/fp/array/unzip.ts。如果你需要的是先合并、再按行做映射直接组合pipe即可需要按列聚合则考虑unzip。zipWith适合合并的同时立刻归约的场景。六、行为验证单元测试给出的保证src/array/zip.spec.ts 用 Vitest 覆盖了核心行为可作为使用时的行为契约it(zips multiple arrays to create a tuple, () { expect(zip([1, 2, 3])).toEqual([[1], [2], [3]]); expect(zip([1, 2, 3], [a, b, c])).toEqual([ [1, a], [2, b], [3, c], ]); // 长度不一致短数组的位置补 undefined expect(zip([1, 2, 3], [a, b])).toEqual([ [1, a], [2, b], [3, undefined], ]); // 3 个数组、4 个数组同样支持 expect(zip([1, 2, 3], [a, b, c], [true, true, false])).toEqual([ [1, a, true], [2, b, true], [3, c, false], ]); expect(zip([1], [a], [true], [null])).toEqual([[1, a, true, null]]); }); it(supports spread operators, () { expect(zip(...[[1], [s], [{ a: 2 }]])).toEqual([[1, s, { a: 2 }]]); });测试确认了几点事实单数组调用把每个元素包成单元素元组支持 24 个数组的重载长度不齐时短数组缺失位补undefined以及zip的 rest 参数天然支持展开运算符spread适合动态数量的数组场景。七、小结es-toolkit/fp 的函数式zip本质上是普通版zip的data-last 薄封装把待合并数组通过闭包前置把主数组留给pipe注入合并规则按索引成组、最长数组定行数、缺失补undefined与 es-toolkit/array 的zip完全一致。得益于 TypeScript 重载从zip()到zip(arr2, arr3, arr4)都能得到精确的元组类型推导底层实现用单次循环求最大长度、双层循环填充矩阵兼顾了正确性与性能。在实际项目中普通直调场景优先使用es-toolkit/array的zip而当你想把多步转换写成自上而下、可读性强的pipe管线时fp版的zip就是对齐多个数组的天然选择需要按键值配对时转向zipObject需要合并后立即归约时使用zipWith需要把合并结果按列拆回时使用unzip。【免费下载链接】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),仅供参考
返回列表