今天回答了 @_bleach 的问题:JS生产嵌套数组(也就是对数组分组)更好的写法。回答的过程当中对 lodash
_.chunk()
产生了好奇,因此分析了一下它的源码,再加上我本身的解决方案,收集了以下一些方案,分享给你们javascript按惯例,我仍是使用 es6 语法,在 Node7 中实验经过java
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; const groupByNum = 3;
由于最近在学习 RxJs,因此顺手作了个 RxJs 解决方案。但这不是重点。不了解或者很了解 RxJs 的请忽略。node
RxJava 很火,其实 ReactiveX 有不少种语言的实现,JavaScript 的实例就是 RxJs,建议学习的同窗直接上 5.0 Beta。不过 RxJs 主要用于异步流处理,因此须要经过回调函数来使用结果。es6
const Rx = require("rxjs"); const out = Rx.Observable.from(data) .bufferCount(groupByNum) .toArray() .do((result) => { console.log(result) }) .subscribe();
上面的 do()
一句能够直接写成 do(console.log)
,写成如今这样的目的是为了让你们看到计算结果在哪。npm
lodash 提供了大量数据处理的方法,_.chunk()
专为分组而生segmentfault
const _ = require("lodash"); const result = _.chunk(data, groupByNum);
在 node 环境也,经过 npm 安装 lodash 以后就能在 node_modules/lodash
目录下找到源码文件 chunk.js
数组
npm install lodash
因此完整的源码不贴了,只看下关键的那一句app
while (index < length) { result[resIndex++] = baseSlice(array, index, (index += size)); }
baseSlice()
是 lodash 对 Array.prototype.slice()
的兼容性实现,能够直接当 slice()
来看。看了这个源码,我有了函数式写法的思路,后面经过 slice() + map()
实现部分详述。异步
像这种,目标数组长度和原数组长度不一致的状况,函数式写法很容易想到 reduce()
函数。只惋惜单纯的 reduce()
作不出来(在 data.length
不能被 groupByNum
整除的时候)函数
function groupArray(data, cols) { const r = data.reduce((r, t) => { r.current.push(t); if (r.current.length === cols) { r.list.push(r.current); r.current = []; } return r; }, { list: [], current: [] }); if (r.current.length) { r.list.push(r.current); } return r.list; } const result = groupArray(data, groupByNum);
reduce()
的初始化对象是 { list: [], current: [] }
,其中 list
是要得计算出来的结果,而 current
是中间变量,用于生成每一个组。
最后因为不有保证 data.length
必定被 groupByNum
整除,因此可能会有一个未完成的 current
没被 push 到 list
当中,因此专门进行了一个判断和处理。所以不能写成函数式写法,有些遗憾。
既然不能用函数式写法,那 forEach()
或者 for ... of
实现就会更容易理解一些。
function groupArray(data, cols) { const list = []; let current = []; // for (t of data) { data.forEach(t => { current.push(t); if (current.length === cols) { list.push(current); current = []; } }); // } // for (t of data) if (current.length) { list.push(current); } return list; }
看到了 _.chunk()
的源码,让我产生了函数式写法的灵感,相比上面的解决方案,更难于理解,不过语法看起来很酷
const result = Array.apply(null, { length: Math.ceil(data.length / groupByNum) }).map((x, i) => { return data.slice(i * groupByNum, (i + 1) * groupByNum); });
Array.apply()
是为了生成一个长度为 Math.ceil(data.length / groupByNum)
的数组做为 map()
的源,map()
不须要这个源的数据,只须要这个源每一个数组的 index
。
Math.ceil()
用于保证在除法计算有余数的时候对商 +1,即循环次数 +1。
而后在算得的循环次数中,经过 slice
返回每一段结果,经过 map()
映射出来,最终生成须要的结果。
数组分组是一个很简单的问题,有不少种方法来处理。本文并非想告诉你们如何处理这个问题,而是将我处理问题的思路分享出来,但愿能对处理各类数据问题带来一点点启发。