- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
split()是 collect.js 中一个专用于"按组数切分集合"的方法:与按固定大小切片的chunk()不同,它接收的是目标组数,由内部自动计算每组应容纳的元素数量,并返回一个"由多个集合组成的集合"。本文基于 docs/api/split.md 展开,并结合 src/methods/split.js 源码与 test/methods/split_test.js 测试用例,完整讲解其用法、分组算法、边界行为以及与chunk()、partition()、forPage()的选型差异。读完本文,你将能在数据分页、负载均衡分组、批次任务切分等场景中熟练使用split(),并理解其返回值结构与配合all()、toArray()等方法的处理方式。
一、方法与基本用法
split()将集合拆分为给定数量的组。官方文档给出的示例(见 docs/api/split.md):
const collection = collect([1, 2, 3, 4, 5]); const groups = collection.split(3); // [[1, 2], [3, 4], [5]]即:5 个元素按 3 组切分,得到[1, 2]、[3, 4]、[5]三组。在 TypeScript 类型定义中(见 index.d.ts),其签名为:
split(numberOfGroups: number): Item[];参数numberOfGroups是期望的组数,返回值在类型层面声明为Item[],而在实际运行时它是一个"集合的集合"(详见第三节)。
二、源码级剖析:分组算法是如何工作的
split()的实现非常简洁,完整源码位于 src/methods/split.js:
'use strict'; module.exports = function split(numberOfGroups) { const itemsPerGroup = Math.round(this.items.length / numberOfGroups); const items = JSON.parse(JSON.stringify(this.items)); const collection = []; for (let iterator = 0; iterator < numberOfGroups; iterator += 1) { collection.push(new this.constructor(items.splice(0, itemsPerGroup))); } return new this.constructor(collection); };整个算法可以拆解为三步:
- 计算每组元素数:
itemsPerGroup = Math.round(this.items.length / numberOfGroups)。注意这里使用的是Math.round(四舍五入)而非Math.floor(向下取整),这是理解分组结果分布的关键。 - 深拷贝原始数据:
JSON.parse(JSON.stringify(this.items))生成一份与原始数据完全独立的副本,后续的splice操作都发生在这份副本上,从而保证split()不会修改原始集合(非破坏性操作)。 - 循环切分并装箱:循环执行
numberOfGroups次,每次用Array.prototype.splice(0, itemsPerGroup)从副本头部取出itemsPerGroup个元素,包装成新的Collection实例(new this.constructor(...)),最后把整个分组数组再包装为一个集合返回。
四舍五入带来的分组分布
以文档示例collect([1, 2, 3, 4, 5]).split(3)为例:
itemsPerGroup = Math.round(5 / 3) = Math.round(1.667) = 2- 第一轮取出
[1, 2],第二轮取出[3, 4],第三轮取出[5] - 结果正是
[[1, 2], [3, 4], [5]]
再对照测试用例 test/methods/split_test.js 中的场景split(2):
itemsPerGroup = Math.round(5 / 2) = Math.round(2.5) = 3- 得到
[1, 2, 3]与[4, 5]两组,而非均分的[1, 2]、[3, 4]、[5]
由此可见,split()并不追求"每组元素数量严格相等",而是先四舍五入得出每组容量,再按顺序依次填充,最后若干组会容纳较少的元素。这在需要"把 N 个元素尽量均匀地分到 M 个桶里"的场景中非常实用。
三、返回值:一个"集合的集合"
split()的返回值不是普通的嵌套数组,而是new this.constructor(collection)——即把每个分组分别包装成Collection后,再整体包装成一个集合。这意味着:
const groups = collect([1, 2, 3, 4, 5]).split(3); groups.all(); // [Collection, Collection, Collection] groups.first().all(); // [1, 2]all()(源码见 src/methods/all.js)返回的是this.items,即底层的分组集合数组。如果需要得到纯嵌套数组,可以借助toArray()(源码见 src/methods/toArray.js),它会递归地把嵌套的Collection实例还原为普通数组:
collect([1, 2, 3, 4, 5]).split(3).toArray(); // [[1, 2], [3, 4], [5]]这种"集合的集合"设计让split()的结果可以直接无缝接入其他 collect.js 方法链,例如配合map()、count()、sum()等对每个分组做二次聚合,无需先手工展开数组。
四、边界情况与行为细节
结合 src/methods/split.js 的代码逻辑,以下几个边界行为值得注意:
1. 组数大于元素数:产生空组
当numberOfGroups大于元素总数时,itemsPerGroup = Math.round(length / numberOfGroups)会得到 1(或更小)。splice(0, 1)在副本耗尽后返回空数组,但循环仍会继续执行完numberOfGroups次,因此结果中会出现空集合。例如collect([1, 2]).split(5)会得到 2 个单元素组加 3 个空组。
2. 组数为 0:返回空集合
当numberOfGroups = 0时,Math.round(length / 0)为Infinity,但for循环的终止条件iterator < 0从一开始就不成立,循环体一次都不会执行,最终返回一个空的集合的集合。
3. 深拷贝的双刃剑
JSON.parse(JSON.stringify(...))保证了原始集合不被修改,但也意味着:
- 集合内的
Date对象会被序列化为字符串,undefined、函数、Symbol等无法被 JSON 序列化的值会被丢弃或转换; - 对于包含复杂对象引用的集合,拷贝后分组内是独立的副本,修改分组内的对象不会影响原始数据。
如果你的数据包含不可序列化值,建议在使用split()前先评估是否需要改用其他方案。
4. 面向数组型集合
split()的实现依赖this.items.length。从 src/index.js 的Collection构造函数看,对象型集合(this.items为普通对象)没有length属性,因此itemsPerGroup会计算为NaN。可以推断,split()主要面向数组型集合;对于对象型集合,如需按键分组,可考虑chunk()(src/methods/chunk.js)等支持对象的方法。
五、与chunk()、partition()、forPage()的选型对比
collect.js 提供了多个"切分"类方法,它们解决的问题各不相同,选型对照如下:
| 方法 | 切分依据 | 典型场景 | 源码位置 |
|---|---|---|---|
split(n) | 指定组数,自动计算每组容量 | 将数据均匀分到固定数量的桶/批次中 | src/methods/split.js |
chunk(size) | 指定每组大小,组数由元素数决定 | 固定条数的批量处理、分页切片 | src/methods/chunk.js |
partition(fn) | 按断言函数分为两组(真/假) | 把数据分成"满足条件"与"不满足"两拨 | src/methods/partition.js |
forPage(page, chunk) | 按页码 + 每页条数取一页 | 手动分页读取指定页 | src/methods/forPage.js |
它们的核心差异在于输入参数的含义:split()与partition()面向"组数/分组逻辑",chunk()与forPage()面向"每组的规模"。例如,把 100 条记录分给 4 个工作节点处理,应使用split(4);而把记录每 25 条一批入库,则应使用chunk(25)。需要把对象按键拆分时,partition()与chunk()提供了对对象型集合的支持,而split()则聚焦数组场景。
六、测试验证
仓库在 test/methods/split_test.js 中为split()提供了两组断言:
it('should return a collection of collections', () => { const collection = collect([1, 2, 3, 4, 5]); expect(collection.split(2)).to.eql(collect([ collect([1, 2, 3]), collect([4, 5]), ])); }); it('should split a collection into the given number of collections', () => { const collection = collect([1, 2, 3, 4, 5]); expect(collection.split(2).all()).to.eql([ collect([1, 2, 3]), collect([4, 5]), ]); });这两条用例分别验证了两个关键契约:
split(2)在 5 个元素上得到[1, 2, 3]与[4, 5]两组,实证了"四舍五入取每组容量、先满后缺"的分布规则(Math.round(5 / 2) = 3);- 返回值是"集合的集合",且
.all()能取到内部的集合数组。
如果要在本地运行验证,可以执行npm test(见 package.json),mocha 会通过 test/tests.js 加载全部方法测试。
七、小结
split()是 collect.js 中"按组数切分"的专用工具,一句话概括其行为:给定组数,自动按四舍五入后的每组容量顺序填充,返回一个由集合组成的集合。使用时的三个要点:
- 参数是组数而非每组大小,
Math.round决定了分组分布"先多后少"; - 返回的是嵌套的
Collection结构,可用all()查看内部集合,用toArray()还原为普通嵌套数组; - 方法基于深拷贝实现、不修改原始集合,但依赖
length属性,适合数组型集合。
掌握split()后,再结合 docs/api/chunk.md、docs/api/partition.md、docs/api/forPage.md 等文档理解其余切分类方法,即可在数据分组场景中做出最合适的选择。
- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
相关推荐
collect.js `mapToDictionary()` 方法全解析:将集合映射为字典分组结构
collect.js mapToDictionary 方法全解析:将集合映射为字典分组结构 mapToDictionary 是 collect.js 提供的一个
开发工具cuML SVM 模块指南:SVC、SVR、LinearSVC 与 LinearSVR 的 GPU 加速支持向量机实战
cuML SVM 模块指南:SVC、SVR、LinearSVC 与 LinearSVR 的 GPU 加速支持向量机实战 本文面向希望用 GPU 加速训练支持向量
开发工具NanoClaw 中移除 OpenCode Agent Provider 的完整卸载指南
NanoClaw 中移除 OpenCode Agent Provider 的完整卸载指南 本篇指南面向已经通过 add opencode https://lin
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考