連番の生成に関して
意外と使用する頻度が高かったりします。他言語であればRange(eg: 1..10)のようなClassやデータ構造が定義されており、簡単に連番の配列を作成することが出来ます。しかし、JavaScript
には連番の配列を簡単に生成するための機能が提供されていないので、一工夫してあげる必要があります。色々と試して結果、4つの方法を思いつきました。内、1つはQiita
で紹介されたいた書き方なので元記事のURLを貼っておきます。
以下、5000回の実行速度の平均速度の速かった順番に書き方を紹介していきます。実行速度の測定は実行開始前と実行終了後の単純な時間差を見ています。測定に使用したコードがこちらです。
const verificater = (execFunc, totalExec=5000) => { const result = mesureAverageElapsedTime(execFunc, totalExec); console.log(`Result: ${result} ms / ${execFunc.toString()}`); } const mesureAverageElapsedTime = (execFunc, totalExec) => { const trylst = range(0, totalExec); const totalTime = trylst.reduce((acc, _) => mesureElapsedTime(execFunc) + acc); return totalTime / totalExec; } const mesureElapsedTime = (execFunc) => { const startTime = Date.now(); execFunc() const endTime = Date.now(); return endTime - startTime }
4つの方法
測定結果:
Result: 0.0006 ms / 5000, () => forRange(0, 31) Result: 0.0012 ms / 5000, () => arrayGen(31) Result: 0.0022 ms / 5000, () => arrayFromGen(31) Result: 0.0128 ms / 5000, () => range(0, 31)
なお、実測を平等に行うために、全て関数として定義しています。
第1位: forを使った生成(forRange
)
Result: 0.0006 ms / 5000
シンプルにfor
文を使った方法です。事前に定義した空配列に値を追加していきます。
const forRange = (a, z) => { const lst = []; for (let i = a; i <= z; i++) { lst.push(i) } return lst; };
第2位: Qiitaで紹介されていた方法(arrayGen
)
0.0012 ms / 5000
Qiita
で紹介されていた今回、最もスマートな記述です。かっこいいですね🎉
const arrayGen = z => [...Array(z+1)].map((_, i) => i);
第3位: Array.fromを使った方法(arrayGenFrom
)
Result: 0.0022 ms / 5000
過去のプロジェクトで自分が使った方法です。Qiita
で紹介されていた方法の劣化版ですね。速度も遅く、使う理由はあまりないかなと思います。
const arrayFromGen = z => Array.from(Array(z+1), (_, i) => i);
第4位: 再帰関数を使った方法(range(0, 31)
)
Result: 0.0128 ms / 5000
再帰関数で書けないかなと思って、無理矢理書いた方法です。スッキリさと裏腹に可読性も悪く、速度も悪いという残念な結果となってしまいました。
const range = (a, z) => _range(a, z, []) const _range = (a, z, acc) => a < z + 1 ? _range(a+1, z, acc.concat(a)) : acc
結論
多分これが一番早いと思います。
const forRange = (a, z) => { const lst = []; for (let i = a; i <= z; i++) { lst.push(i) } return lst; };
JavaScript
を書く人はfor
文を異常に避ける方がいますが、この程度の処理だったら、for
文で全然良いのかなと思います。
参考になればと思います。
追記
Result: 0.0132 ms / () => range(0, 31)
Result: 0.0012 ms / () => neoRange(0, 31)
再帰関数を三項演算子を使わずに、concat
からpush
に乗り換えて実装してみたら、めちゃくちゃ早くなりました。どうやら、concat
の処理が毎回、新しく配列を生成するので、かなりのボトルネックになっていたようです。
const neoRange = (a, z) => _neoRange(a, z, []); const _neoRange = (a, z, acc) => { if (a < z + 1) { acc.push(a) // const next = acc.concat(a) return _neoRange(a + 1, z, acc) } else { return acc; } }