
es-toolkit/fp 中的 zipWith在 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-toolkitzipWith是 es-toolkit 函数式编程入口es-toolkit/fp提供的一个数据末尾data-last算子它接收一组预先配置好的数组和一个combine组合函数返回一个等待数据传入的函数从而可以无缝嵌入pipe组成的自上而下变换流水线。本文将围绕docs/fp/reference/zipWith.md展开结合 fp 变体实现 与 底层数组版实现 的源码说明其调用约定、combine回调的参数顺序、长度不齐时的undefined补齐行为以及与普通es-toolkit数组版zipWith的取舍关系。读完你可以在自己的pipe链中直接写出按索引对齐的多数组合并逻辑并理解其类型重载与实现原理。一、什么是es-toolkit/fp版本的zipWithes-toolkit/fp是 es-toolkit 的函数式编程入口其核心理念是每一个 FP 函数都先用配置参数调用例如zipWith([10, 20], (a, b) a b)返回一个“等待数据”的函数再由pipe把数据喂进去将嵌套调用改写为自上而下、顺序可读的流水线。FP 版zipWith正是这一模式的典型代表。它的文档签名如下const result pipe(array, zipWith(...arrs, combine));即被 pipe 传入的数组作为参与合并的第一个数组zipWith配置的若干arrs数组紧随其后combine函数负责把同一索引位置上的元素合并成一个新值最终返回一个与最长输入数组等长的结果数组。官方文档在开头的提示块中明确给出了使用建议在普通代码中优先使用 es-toolkit 原始的zipWith只有在使用pipe组合变换时才使用本fp变体。二、在pipe流水线中的用法zipWith会把被 pipe 的数组和配置数组在相同索引处的值传递给combine。如果数组长度不同缺失位置的值会以undefined传入。2.1 等长数组按索引逐位合并import { pipe, zipWith } from es-toolkit/fp; pipe( [1, 2], zipWith([10, 20], (a, b) a b) ); // [11, 22]这里[1, 2]是 pipe 传入的数据[10, 20]是配置数组。combine收到(1, 10)得到11收到(2, 20)得到22。2.2 长度不齐缺失值以undefined补齐import { pipe, zipWith } from es-toolkit/fp; pipe( [1, 2, 3], zipWith([10], (a, b 0) a b) ); // [11, 2, 3]被 pipe 的数组长度为 3配置数组[10]只有 1 个元素。合并时第一个位置收到(1, 10)得到11第二、三个位置b均为undefined因此默认参数b 0生效得到2和3。这正是“按最长数组长度对齐、较短数组缺失位以undefined填充”的语义。2.3 参数与返回值根据文档FP 版zipWith的完整签名如下参数arrsArrayreadonly unknown[]要与被 pipe 数组合并的若干数组。combine(...values: unknown[]) R接收同一索引处的各数组值随后还会收到当前索引并返回一个新的合并值。返回值(array: readonly T[]) R[]一个把被 pipe 数组映射为合并结果数组的函数。注意一个容易被忽略的细节combine的最后一个参数是当前索引。这一点与es-toolkit数组版zipWith一致也在src/array/zipWith.spec.ts的测试中得到验证const result zipWith([10, 20, 30], [1, 2, 3], (a, b, index) a b index); expect(result).toEqual([11, 23, 35]);10 1 0 11、20 2 1 23、30 3 2 35可以看到索引从 0 开始按位递增地传给combine。三、从源码看实现原理3.1 fp 变体薄封装 参数前置src/fp/array/zipWith.ts的实现非常简洁本质上是对数组版zipWith的参数重排封装import { zipWith as zipWithToolkit } from ../../array/zipWith.ts; export function zipWith(...rest: unknown[]): unknown { return function (array: readonly unknown[]): unknown[] { return (zipWithToolkit as (array: readonly unknown[], ...rest: unknown[]) unknown[])( array, ...rest ); }; }核心逻辑只有一句把“等待数据”的闭包返回出去等到 pipe 真正传入数组时把该数组放在第一位与配置参数一起转发给数组版zipWith。这正是“数据末尾”风格的实现方式——数据参数array永远在最后才出现。3.2 底层实现最长长度对齐 索引传递真正的合并算法在src/array/zipWith.ts的实现签名第 108-121 行export function zipWithT, R(arr1: readonly T[], ...rest: any[]): R[] { const arrs [arr1, ...rest.slice(0, -1)]; const combine rest[rest.length - 1] as (...items: any[]) R; const maxIndex Math.max(...arrs.map(arr arr.length)); const result: R[] Array(maxIndex); for (let i 0; i maxIndex; i) { const elements: T[] arrs.map(arr arr[i]); result[i] combine(...elements, i); } return result; }从源码可以提炼出三个关键实现事实结果数组长度 最长输入数组的长度maxIndex取所有参与数组长度的最大值结果数组按该长度预先分配Array(maxIndex)。缺失值天然为undefined遍历时用arrs.map(arr arr[i])逐位取值较短数组在越界索引上直接读取到undefined与文档描述的“missing values are passed asundefined”完全吻合。索引是combine的最后一个参数调用形式是combine(...elements, i)即“各数组同索引的值随后是索引本身”与文档参数说明一一对应。3.3 类型重载最多支持 4 个数组参与合并为了给 TypeScript 用户提供精确的类型推断fp 变体与数组版都提供了 4 个重载src/fp/array/zipWith.ts第 17-68 行zipWith(combine)仅对 pipe 数组按(item, index)变换等价于带索引的mapzipWith(arr2, combine)pipe 数组 1 个配置数组combine收到(item1, item2, index)zipWith(arr2, arr3, combine)pipe 数组 2 个配置数组combine收到(item1, item2, item3, index)zipWith(arr2, arr3, arr4, combine)pipe 数组 3 个配置数组combine收到(item1, item2, item3, item4, index)。从源码结构看运行时的...rest实参并没有强制限制数量上限但类型层面通过重载把“最多 4 个数组含被 pipe 的数组”作为受支持的组合方式。每多一个配置数组combine就会多收一个同索引元素参数且索引始终位于参数列表的最后一位。四、FP 版与普通数组版zipWith的对比es-toolkit 同时提供两个入口的zipWith使用时应注意区分对比维度es-toolkit/fp的zipWithes-toolkit/array的zipWith导入路径import { zipWith } from es-toolkit/fpimport { zipWith } from es-toolkit/array数据参数位置数据最后传入配合pipe数据参数直接作为第一参数传入典型调用pipe([1, 2], zipWith([10, 20], (a, b) a b))zipWith([1, 2], [10, 20], (a, b) a b)适用场景在pipe流水线中作为一步变换普通命令式/函数直接调用两个入口共用同一份底层实现fp 变体只是把参数重排后转发因此合并语义完全一致按最长数组对齐、缺失位补undefined、combine末位参数为索引。参考普通数组版的文档与示例import { zipWith } from es-toolkit/array; zipWith([1, 2, 3], [4, 5, 6], (a, b) a b); // [5, 7, 9] zipWith([1, 2], [10, 20, 30], (a, b) (a ?? 0) (b ?? 0)); // [11, 22, 30]对比可见同一份合并逻辑在es-toolkit/fp中被包装成“配置先行、数据后置”的算子形态从而适配pipe的自上而下组合风格。五、结合 pipe 与其他算子组合使用zipWith是pipe流水线中的普通一步。由于 fp 版每个函数都返回“等待数据”的函数zipWith可以和其他算子如map、filter、take等自由串联import { pipe, map, zipWith } from es-toolkit/fp; pipe( [1, 2, 3], zipWith([10, 20, 30], (a, b) a b), // [11, 22, 33] map(x x * 2) // [22, 44, 66] ); // [22, 44, 66]关于pipe本身src/fp/pipe.ts会把连续的惰性算子map、filter、take等融合为单趟遍历元素逐个穿过所有步骤take满足后立即提前终止从而避免中间数组分配。不过zipWith属于一次性消费整个数组的变换它会把上一步的数组结果整体取用后继续向下一步传递这并不影响它作为“数据末尾”算子与pipe协作的基本形态。更完整的 FP 入口背景可参考docs/fp/intro.md。六、测试验证仓库用 Vitest 为 fp 版zipWith编写了专项测试src/fp/array/zipWith.spec.ts直接印证了本文提到的两个核心行为import { describe, expect, it } from vitest; import { zipWith } from ./zipWith.ts; import { pipe } from ../pipe.ts; describe(zipWith, () { it(works in a pipe, () { expect( pipe( [1, 2], zipWith([10, 20], (a, b) a b) ) ).toEqual([11, 22]); }); it(uses the longest input length and passes undefined for missing values, () { expect( pipe( [1, 2, 3], zipWith([a, b] as readonly unknown[], (a, b, index) [a, b, index]) ) ).toEqual([ [1, a, 0], [2, b, 1], [3, undefined, 2], ]); }); });第二个用例尤其值得注意它同时验证了“结果长度取最长输入长度”结果为 3 项、“缺失值以undefined传递”第三项b为undefined和“combine末位收到索引”index依次为 0、1、2三个语义与官方文档的描述完全一致。七、小结es-toolkit/fp的zipWith是一个数据末尾算子配置参数数组 combine先行数据在pipe中最后传入返回(array: readonly T[]) R[]。合并语义按索引逐位合并结果长度为最长输入数组的长度较短数组缺失位以undefined传入combine的参数顺序为“各数组同索引值 当前索引”。类型重载支持 pipe 数组 0 到 3 个配置数组共最多 4 个数组参与索引始终是combine的最后一个参数。与es-toolkit/array的zipWith共用底层实现仅调用形态不同普通代码优先用数组版组合流水线时用 fp 版。实现细节可查阅src/fp/array/zipWith.ts薄封装与src/array/zipWith.ts核心算法行为验证见src/fp/array/zipWith.spec.ts。【免费下载链接】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),仅供参考