1. はじめに
ドットインストールの「JavaScriptでビンゴシートを作ろう」を写経しながら学習しました。配列の生成・ランダム抽出・DOM描画という3つの処理が組み合わさっており、配列操作の実践的な使い方を整理するために記事にまとめました。
2. 完成コード
JavaScript
"use strict";
{
// 1列分の数字を生成してランダムに5個返す関数
function createColumn(col) {
const source = [];
// 列番号に応じた15個の数字プールを作る
for (let i = 0; i < 15; i++) {
source[i] = i + 1 + 15 * col;
}
const column = [];
// プールからランダムに1個ずつ取り出して5個にする
for (let i = 0; i < 5; i++) {
column[i] = source.splice(
Math.floor(Math.random() * source.length),
1,
)[0];
}
return column;
}
// 5列分まとめて生成しFREEマスを設定する関数
function createColumns() {
const columns = [];
for (let i = 0; i < 5; i++) {
columns[i] = createColumn(i);
}
// N列(index:2)の中央(index:2)をFREEに固定
columns[2][2] = "FREE";
console.table(columns);
return columns;
}
// columnsの配列をDOMのテーブルに描画する関数
function renderBingo(columns) {
// 外側が行・内側が列のループでtr・tdを生成する
for (let row = 0; row < 5; row++) {
const tr = document.createElement("tr");
for (let col = 0; col < 5; col++) {
const td = document.createElement("td");
// columns[col][row]で列優先→行優先に変換して取り出す
td.textContent = columns[col][row];
tr.appendChild(td);
}
document.querySelector("tbody").appendChild(tr);
}
}
const columns = createColumns();
renderBingo(columns);
}
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Bingo</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<table>
<thead>
<tr>
<th>B</th>
<th>I</th>
<th>N</th>
<th>G</th>
<th>O</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script src="js/main.js"></script>
</body>
</html>
CSS(
body {
font-family: 'Courier New', monospace;
}
th, td {
background: pink;
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
}
3. ビンゴシートの設計を理解する
ビンゴカードは5列×5行の構成で、各列に割り当てられる数字の範囲が決まっているみたいです。B列が1〜15、I列が16〜30、N列が31〜45、G列が46〜60、O列が61〜75という構成です。
3.1 列ごとの数字プール(source配列)を作る
列番号 col を受け取り、その列に対応する15個の数字プールを source 配列として生成します。
// 列番号に対応した15個の数字プールを生成する
const source = [];
for (let i = 0; i < 15; i++) {
// i + 1 + 15 * col で列ごとにずらした数値になる
source[i] = i + 1 + 15 * col;
}
col が 0 のとき source は [1, 2, ..., 15]、col が 1 のとき [16, 17, ..., 30] になるみたいです。
3.2 splice でランダムに5個抽出する
15個のプールからランダムに5個だけ取り出す処理に splice を使っています。
// sourceからランダムに1要素ずつ取り出してcolumnに積む
const column = [];
for (let i = 0; i < 5; i++) {
column[i] = source.splice(
// Math.random() * source.length でランダムなインデックスを生成
Math.floor(Math.random() * source.length),
1,
)[0];
}
splice(インデックス, 1) は指定した位置の要素を取り除いて返す処理で、戻り値が配列なので [0] で値を取り出しているみたいです。取り出した要素は source から消えるため、同じ数字が重複しない仕組みになっていると理解しました。
splice の戻り値は「取り除いた要素の配列」です。削除数を 1 にしても配列で返ってくるので、末尾に [0] をつけて値を取り出しています。
4. 5列まとめて生成してFREEマスを設定する
createColumn を5回呼んで全列をまとめる関数が createColumns です。
// 5列分の配列をまとめてcolumnsに格納し、中央をFREEにする
const columns = [];
for (let i = 0; i < 5; i++) {
columns[i] = createColumn(i);
}
columns[2][2] = "FREE";
console.table(columns);
columns[2][2] は「3列目の3行目」を指していて、ビンゴカード中央のFREEマスに対応しているみたいです。
console.table を使うと二次元配列を表形式で確認できるみたいです。
【反転前:console.table(columns) の出力イメージ】
columns は columns[列インデックス][行インデックス] の順で格納されているため、console.table では columns[0] = B列の5個が1行目に横並びで表示されるみたいです。
| インデックス | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 3 | 11 | 7 | 1 | 14 |
| 1 | 22 | 17 | 29 | 25 | 19 |
| 2 | 38 | 43 | FREE | 35 | 41 |
| 3 | 54 | 48 | 60 | 51 | 57 |
| 4 | 70 | 63 | 67 | 72 | 65 |
1行目(インデックス0)にB列(1〜15の範囲)の値が横に並びます。columns[0] がB列全体(5個)、columns[1] がI列全体(5個)を表しています。
【反転後:画面上のビンゴカード(renderBingo による描画結果)】
renderBingo で columns[col][row] と取り出すことで行と列が入れ替わり、B列(1〜15の範囲)の値が1列目に縦に並ぶ形になるみたいです。
| インデックス | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 3 | 22 | 38 | 54 | 70 |
| 1 | 11 | 17 | 43 | 48 | 63 |
| 2 | 7 | 29 | FREE | 60 | 67 |
| 3 | 1 | 25 | 35 | 51 | 72 |
| 4 | 14 | 19 | 41 | 57 | 65 |
反転前の console.table では1行目にB列(1〜15の範囲)の値が横に並びます。反転後の画面では1列目にB列の値が縦に並びます。columns[col][row] という順番で取り出すことでこの変換が行われているみたいです。
5. 配列からDOMを生成して描画する
// 行と列のループを使いtbodyにtr・tdを順番に追加する
for (let row = 0; row < 5; row++) {
const tr = document.createElement("tr");
for (let col = 0; col < 5; col++) {
const td = document.createElement("td");
// columns[col][row]で列優先配列を行優先に変換して取り出す
td.textContent = columns[col][row];
tr.appendChild(td);
}
document.querySelector("tbody").appendChild(tr);
}
columns[col][row] という順番で取り出すことで、列優先に格納された配列を行優先の描画に変換しているみたいです。ここのインデックスを columns[row][col] にしてしまうと数字の並びがおかしくなるので要注意と理解しました。
まとめ
今回の実装で理解できたポイントをまとめました。
-
splice(index, 1)[0]で配列からランダムに1要素を取り出しつつ元の配列を縮小できるみたいです -
columnsはcolumns[列][行]の順で格納されており、console.tableでは1行目にB列(1〜15の範囲)の値が横に並ぶ形で表示されると理解しました -
renderBingo内のcolumns[col][row]によって行と列が入れ替わり、画面では1列目にB列の値が縦に並ぶ形になるみたいです -
console.tableは二次元配列のデバッグに非常に有効で、構造の確認がしやすいと感じました -
columns[2][2] = "FREE"のように、生成後に特定マスの値を上書きする発想がシンプルで参考になりました
