今天回答了 @_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()
映射出來,最終生成須要的結果。
數組分組是一個很簡單的問題,有不少種方法來處理。本文並非想告訴你們如何處理這個問題,而是將我處理問題的思路分享出來,但願能對處理各類數據問題帶來一點點啓發。