如何基于指定函数对数组分组?求JS链式调用实现代码逐步骤详解
数组分组函数逻辑逐步骤解析
先给出原函数代码:
const group_By = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { acc[val] = (acc[val] || []).concat(arr[i]); return acc; }, {});
函数基础说明
该函数接收两个参数:
arr:待分组的原始数组fn:分组规则,支持两种传入形式:- 自定义函数:返回值作为每个元素的分组key
- 字符串:指定原始数组元素的属性名,用该属性值作为分组key
链式调用分步拆解
整个函数的逻辑分为map生成分组key、reduce聚合分组两个阶段,链式调用依次执行:
阶段1:map生成所有元素的分组key
arr.map(typeof fn === 'function' ? fn : val => val[fn])
这段是map方法的回调逻辑,通过三元表达式适配两种分组规则:
- 如果传入的
fn是函数,直接用该函数处理数组的每个元素,得到对应分组key - 如果传入的
fn不是函数(通常为属性名字符串),自动生成回调函数,取每个元素对应属性的值作为分组key
示例:若原始数组为[{category:'a', id:1}, {category:'b', id:2}],传入fn = 'category'时,该阶段返回结果为['a', 'b']。
阶段2:reduce完成分组聚合
map执行完返回的key数组,直接调用reduce做聚合,reduce初始值为空对象{},回调的三个参数分别是:
acc:累加器,存储当前已经生成的分组结果val:当前遍历到的分组key(即上一步map的返回值元素)i:当前遍历的索引值,对应原始数组的元素索引
每一轮遍历的执行逻辑:
- 先判断累加器中是否存在当前key对应的分组:
acc[val] || [],若不存在则默认赋值空数组 - 将原始数组对应索引的元素
arr[i]追加到该分组数组中,concat方法会返回新数组,直接赋值给acc[val] - 返回累加器进入下一轮遍历
完整运行示例
// 测试数据 const goods = [ {type: 'fruit', name: 'apple'}, {type: 'vegetable', name: 'carrot'}, {type: 'fruit', name: 'banana'}, ] // 调用函数,按type属性分组 const result = group_By(goods, 'type');
执行过程:
- map阶段返回key数组:
['fruit', 'vegetable', 'fruit'] - reduce初始值为
{}- 第1轮:key为
fruit,原数组索引0的元素是苹果,累加器新增fruit属性,值为[{type: 'fruit', name: 'apple'}] - 第2轮:key为
vegetable,原数组索引1的元素是胡萝卜,累加器新增vegetable属性,值为[{type: 'vegetable', name: 'carrot'}] - 第3轮:key为
fruit,原数组索引2的元素是香蕉,追加到已有的fruit分组数组中
- 第1轮:key为
- 最终返回的分组结果:
{ fruit: [ {type: 'fruit', name: 'apple'}, {type: 'fruit', name: 'banana'} ], vegetable: [ {type: 'vegetable', name: 'carrot'} ] }
内容的提问来源于stack exchange,提问作者Mrblue
相关产品推荐
相关产品推荐

