所定の回数だけインデクスに応じて何か処理をしてそれぞれの結果を配列にしたいという処理をサクッと書きたいんだけど、 JavaScriptで整数配列を簡単に生成する方法が覚えられなくて毎度ウェブ検索のお世話になっていたので調べた。
結論
スプレッドしてmap [...Array(N)].map((_, i) => i)
が簡潔さと速度の面でよいと思う。
完
.
.
(末尾にベンチマークの実行あり)
前書き
ループを自前で回せば一応できるんだけど、for
文を書くのが面倒なのでサクッとmap
したい。
Pythonではrange
、Rubyでは...
でRange
オブジェクトなるものが簡単に生成できる。
JavaScriptではそのようなものは無いようで、自作する必要がある。
候補
実際には整数列を加工した結果を得たいので、range
の代替を作るだけじゃなくて実際になにか計算させてみる(ここでは自乗i * i
としてみた)。
何回か実行して処理にかかる経過時間を performance.now()
で取得して平均を測る。
for-push:愚直にforループを書いて、結果をpush
const array = new Array() |
prealloc-for-store:容量を指定して配列を確保、愚直にforループを書いて結果を格納
const array = new Array(N) |
array.push.map:配列にpush
して整数列を生成し、mapする
function array_push(n) { |
array.prealloc.map:容量を指定して配列を確保・格納してmap
function array_prealloc(n) { |
array.from.map:Array.fromを使う
Array(N)
または new Array(N)
で容量を指定して配列が確保できるが、それに対して map
を使っても意図した結果にならない:
Array(5).map((v, i) => i) //=> [ <5 empty items> ] |
なんか謎の挙動なんだけど、
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/Array 注: これは
arrayLength
個の空のスロットを持つ配列であり、実際にundefined
の値が入ったスロットではありません
とのこと。Array.fromを使ってようやく使える:
Array.from(Array(N)).map((_, i) => i * i) |
map
に与えた関数の第一引数に値が渡されるがundefined
なので無視して、第二引数に渡されるインデクスを利用する。
array.from:Array.fromに関数を渡す
Array.fromに関数を渡せる:
Array.from(Array(N), (_, i) => i * i) |
array.from.length:Array.fromにlengthを与える
Array.fromにはlength
を指定して生成する方法もあるとのこと:
Array.from({length: N}, (_, i) => i * i) |
array.fill.map:Array.fillで実体化する
Array.fillで空のスロットを実体化する:
Array(N).fill().map((_, i) => i * i) |
spread.map:スプレッド構文
array.from.mapと同じだが、スプレッド構文 [...]
を使う:
[...Array(N)].map((_, i) => i * i) |
spread.keys
インデクスからmapで加工するのであれば上のspread.mapでいいんだけど、 一応Array.prototype.keys() にスプレッドの適用も試してみる:
[...Array(N).keys()] |
これだけはmapせずに、整数列を得るだけ。
spread.generator:ジェネレータを使用する
余計な呼び出しコストが必要で明らかに色々ペナルティ多そうなので言わずもがなだが、一応ジェネレータを使った場合も調べてみる:
function *range_gen(n) { |
ジェネレータはfunction*
としてしか書けず、アロー・無名関数では書けないとのこと。
実際に走らせて負荷を確認
考察
- 配列に順に
push
するより、あらかじめサイズを指定して確保してインデクスで格納したほうがよい - 配列のサイズが大きくなると差が開く
- 環境によっても結果が変わってくるので、書きやすい方法を使うのが一番という身も蓋もない結果に…
- にしても、array.from.mapよりもarray.fromやarray.from.lengthが遅いのが腑に落ちない…