LINDORのAdvent Calendar(本物)の10日目を開けたところ。
配列とかおれおれAdvent Calendar2018 – 10日目

最初に結論なんだけど、たぶん [...Array(length)].map(fn) が良いかなと。

const arr = [...Array(3)].map((_, i) => i);
console.log(arr); // => [ 0, 1, 2 ]
console.log(arr.length); // => 3

... で展開

配列オブジェクトは反復可能 (iterable) なので、スプレッド構文 ... で展開してやることができます。

const arr = [...Array(3)];
console.log(arr); // => [ undefined, undefined, undefined ]
console.log(arr.length); // => 3

短いし良さそう。

ただ記号の組み合わせになるので、初見でぎょっとするかも? すぐ慣れるとは思うけれど。

map() と組み合わせて初期値

初期値も設定できます。初期値じゃないけど実質初期値。

const arr = [...Array(3)].map(() => 0);
console.log(arr); // => [ 0, 0, 0 ]

オブジェクトを初期値にする場合は注意

オブジェクトを作る場合、丸括弧 () で括るのを忘れないように。 => に続けて波括弧 {} を書くと違う意味になるので。

const arr = [...Array(3)].map((_, i) => { id: i + 1 });
console.log(arr); // => [ undefined, undefined, undefined ]
const arr = [...Array(3)].map((_, i) => ({ id: i + 1 }));
console.log(arr); // => [ { id: 1 }, { id: 2 }, { id: 3 } ]

他のやり方も同じね。

new Array(length)

これが一番素直なやりかたではあるが、項目が空になってしまうという問題がある。

const arr = Array(3);
console.log(arr); // => [ <3 empty items> ]

arr.forEach((value) => {
  console.log(value); // ←一度も呼ばれない
});

ここでいう空は undefined が入っているわけではなく本当に空なので、 forEach()map() が利きません。

というわけでだいたいの場合、こいつを起点にしてもうちょっとずつコードをこねこねしてやる必要があります。

new

はあってもなくても同じです。

const arr = new Array(3);

詳しくはこちら。

Array.from()

初見のわかりやすさならこちらか。

const arr = Array.from({ length: 3 });
console.log(arr); // => [ undefined, undefined, undefined ]
console.log(arr.length); // => 3

可読性は悪くないが、ちょっと記述量が多いような?

初期値で埋める

第2引数へマップ関数与えられるので、 undefined 以外を設定したい場合に大変便利。

const arr = Array.from({ length: 3 }, () => 0);
console.log(arr); // => [ 0, 0, 0 ]

fill()

map() 等は空の項目を飛ばすんだけど、 fill() は飛ばさずに埋めてくれます。

const arr = Array(3).fill(0);
console.log(arr); // => [ 0, 0, 0 ]
console.log(arr.length); // => 3

undefined 以外にも任意の値で生成できるし、ハック的な不明瞭さもないし、良いんじゃないすかね。

ただし、オブジェクトを入れるのはだめです。

オブジェクトで埋めちゃだめ

各要素が同じオブジェクトのインスタンスを指してしまいます。

const arr = Array(3).fill({ a: 1 });

arr[0].a = 2;
console.log(arr[0]); // => { a: 2 }
console.log(arr[1]); // => { a: 2 }
console.log(arr[0] === arr[1]); // => true

というわけで、やるならこう。

const arr = Array(3).fill().map(() => ({ a: 1 }));

結局長くなってしまった。 しかもメソッドを連鎖して呼び出すので、一行で書くには見た目がちょっと。

join() からの split()

一度文字列に変換してから、配列へ再変換します。

const arr = Array(3).join(',').split(',');
console.log(arr); // => [ '', '', '' ]
console.log(arr.length); // => 3

要素は空文字列 '' になります。

二段階あるのがハックっぽくて可読性低め。

apply()

関数オブジェクトのメソッド apply() です。

const arr = Array.apply(null, Array(3));
console.log(arr.length); // => 3
console.log(arr); // => [ undefined, undefined, undefined ]

利点は古い環境でも使えるところ。

for で頑張る

いやあこれはちょっと。

const arr = Array(3);
for (let i = 0; i < arr.length; i++) {
  arr[i] = undefined;
}

console.log(arr); // => [ undefined, undefined, undefined ]
console.log(arr.length); // => 3

おしまい

というわけで、 [...Array(3)].map(fn) がよろしいかと存じます。

関連

参考