1次元配列の初期化
配列を new Array
で生成すると、各要素が undefined
の配列が得られます。
ES2015では fill
メソッドを用いることで、引数の値で初期化してくれます。
var arr = (new Array(3)).fill(0);
2次元配列の初期化
それでは、3×3の2次元配列を0で初期化するコードを紹介します。
従来の方法
これまでのJavaScriptでは、配列を2重ループを用いて初期化するのが定番でしたね。
var x, y;
var tbl = new Array(3);
for(y = 0; y < 3; y++) {
tbl[y] = new Array(3);
for(x = 0; x < 3; x++) {
tbl[y][x] = 0;
}
}
実際のところ、これで充分なのですが、ES2015の書式でもっとすっきりさせてみたいと思います。
失敗例
var tbl = (new Array(3)).fill((new Array(3)).fill(0));
冒頭の1次元配列でのfillを使った書き方に倣って、fillを2重にしてみました。
最初書いてみて、これで万事OK、と思っていたのですが、実際このコードには欠陥があります。
var tbl = (new Array(3)).fill((new Array(3)).fill(0));
tbl[1][1] = 5;
console.log(tbl[1][1]); // => 5
console.log(tbl[2][1]); // => 5
このコードのように、2次元配列内の要素を1つ上書きしただけのはずなのに、
違う行の要素まで書き換わってしまっています。
それもそのはず、fillの中に入れた配列がプリミティブ値でないため、参照が各行とも同じになってしまっているのです。
以下のコードとやってることは同じです。
var row = (new Array(3)).fill(0);
var tbl = [row, row, row];
row[1] = 5;
console.log(tbl[1][1]); // => 5
console.log(tbl[2][1]); // => 5
正しい書き方
方法1
最初に試した方法を利用します。
var tbl = JSON.parse(JSON.stringify((new Array(3)).fill((new Array(3)).fill(0))));
配列をいったんJSON化して、それを再度オブジェクト化することによって、全ての配列が別オブジェクトになります。
方法2
いったん1×3の配列を作り、それぞれの行を forEach
を使って配列にすげ替えます。
var tbl = (new Array(3)).fill(0);
tbl.forEach((_, i) => {
tbl[i] = (new Array(3)).fill(0);
});
あまり効率的な感じはしませんね…。
方法3
これが一番素直でよいでしょう。
var tbl = new Array(3);
for(let y = 0; y < 3; y++) {
tbl[y] = new Array(3).fill(0);
}
ES2015の記法で1重ループに抑えることができました。
他にもっとすっきりした初期化方法があればコメントください。
追記 すっきりした汎用コード
現時点でお二方からコメントくださって、両方ともES2015らしい書き方だと思ったので、行数列数を任意に変えられる汎用コードもご用意しました。
generate2DArray1 = (m, n, val = 0) => {
return Array.from(new Array(m), _ => new Array(n).fill(val));
};
generate2DArray2 = (m, n, val = 0) => {
return [...Array(m)].map(_ => Array(n).fill(val));
}