今天回答了 @_bleach 的問(wèn)題:JS生產(chǎn)嵌套數(shù)組(也就是對(duì)數(shù)組分組)更好的寫(xiě)法?;卮鸬倪^(guò)程中對(duì) lodash
_.chunk()
產(chǎn)生了好奇,所以分析了一下它的源碼,再加上我自己的解決方案,收集了如下一些方案,分享給大家按慣例,我還是使用 es6 語(yǔ)法,在 Node7 中實(shí)驗(yàn)通過(guò)
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
const groupByNum = 3;
因?yàn)樽罱趯W(xué)習(xí) RxJs,所以順手做了個(gè) RxJs 解決方案。但這不是重點(diǎn)。不了解或者很了解 RxJs 的請(qǐng)忽略。
RxJava 很火,其實(shí) ReactiveX 有很多種語(yǔ)言的實(shí)現(xiàn),JavaScript 的實(shí)例就是 RxJs,建議學(xué)習(xí)的同學(xué)直接上 5.0 Beta。不過(guò) RxJs 主要用于異步流處理,所以需要通過(guò)回調(diào)函數(shù)來(lái)使用結(jié)果。
const Rx = require("rxjs");
const out = Rx.Observable.from(data)
.bufferCount(groupByNum)
.toArray()
.do((result) => {
console.log(result)
})
.subscribe();
上面的 do()
一句可以直接寫(xiě)成 do(console.log)
,寫(xiě)成現(xiàn)在這樣的目的是為了讓大家看到計(jì)算結(jié)果在哪。
lodash 提供了大量數(shù)據(jù)處理的方法,_.chunk()
專為分組而生
const _ = require("lodash");
const result = _.chunk(data, groupByNum);
在 node 環(huán)境也,通過(guò) npm 安裝 lodash 之后就能在 node_modules/lodash
目錄下找到源碼文件 chunk.js
npm install lodash
所以完整的源碼不貼了,只看下關(guān)鍵的那一句
while (index < length) {
result[resIndex++] = baseSlice(array, index, (index += size));
}
baseSlice()
是 lodash 對(duì) Array.prototype.slice()
的兼容性實(shí)現(xiàn),可以直接當(dāng) slice()
來(lái)看??戳诉@個(gè)源碼,我有了函數(shù)式寫(xiě)法的思路,后面通過(guò) slice() + map()
實(shí)現(xiàn)部分詳述。
像這種,目標(biāo)數(shù)組長(zhǎng)度和原數(shù)組長(zhǎng)度不一致的情況,函數(shù)式寫(xiě)法很容易想到 reduce()
函數(shù)。只可惜單純的 reduce()
做不出來(lái)(在 data.length
不能被 groupByNum
整除的時(shí)候)
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()
的初始化對(duì)象是 { list: [], current: [] }
,其中 list
是要得計(jì)算出來(lái)的結(jié)果,而 current
是中間變量,用于生成每個(gè)組。
最后由于不有保證 data.length
一定被 groupByNum
整除,所以可能會(huì)有一個(gè)未完成的 current
沒(méi)被 push 到 list
當(dāng)中,所以專門(mén)進(jìn)行了一個(gè)判斷和處理。因此不能寫(xiě)成函數(shù)式寫(xiě)法,有些遺憾。
既然不能用函數(shù)式寫(xiě)法,那 forEach()
或者 for ... of
實(shí)現(xiàn)就會(huì)更容易理解一些。
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()
的源碼,讓我產(chǎn)生了函數(shù)式寫(xiě)法的靈感,相比上面的解決方案,更難于理解,不過(guò)語(yǔ)法看起來(lái)很酷
const result = Array.apply(null, {
length: Math.ceil(data.length / groupByNum)
}).map((x, i) => {
return data.slice(i * groupByNum, (i + 1) * groupByNum);
});
Array.apply()
是為了生成一個(gè)長(zhǎng)度為 Math.ceil(data.length / groupByNum)
的數(shù)組作為 map()
的源,map()
不需要這個(gè)源的數(shù)據(jù),只需要這個(gè)源每個(gè)數(shù)組的 index
。
Math.ceil()
用于保證在除法計(jì)算有余數(shù)的時(shí)候?qū)ι?+1,即循環(huán)次數(shù) +1。
然后在算得的循環(huán)次數(shù)中,通過(guò) slice
返回每一段結(jié)果,通過(guò) map()
映射出來(lái),最終生成需要的結(jié)果。
數(shù)組分組是一個(gè)很簡(jiǎn)單的問(wèn)題,有很多種方法來(lái)處理。本文并不是想告訴大家如何處理這個(gè)問(wèn)題,而是將我處理問(wèn)題的思路分享出來(lái),希望能對(duì)處理各種數(shù)據(jù)問(wèn)題帶來(lái)一點(diǎn)點(diǎn)啟發(fā)。
更多建議: