入れ子のループで掛算表を作る
入れ子のループで掛算表を作る とは
ループの中にループを書く「ネストループ」を学び、九九の二次元配列を作ってみよう。本レッスンでは、入れ子のループで掛算表を作る の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
ループの中にループを書く「ネストループ」
ここまで学んだループは「配列を 1 回走査する」「1 から n まで 1 回数える」など、1 本のループで完結するものでした。今度は「ループの中にもう 1 つループを書く」というテクニック、ネストループ を学びます。九九の掛算表や、二次元グリッド (将棋盤・チェス盤・座標平面) の処理など、応用範囲はかなり広いです。
ネストループは初心者にとって「頭がぐるぐるする最初の関門」ですが、コツを掴めば意外と素直な構造です。「外側のループが 1 回進むたびに、内側のループが最初から最後まで回る」、この一文を頭に叩き込めば、後は手を動かすだけで身につきます。
まず、九九の 3 x 3 部分を出力するコードを見てみましょう。
JavaScript
function printTable() {
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
// i x j を出力
}
}
}これを実行すると、内側のループ (j) は i = 1 のときに j = 1, 2, 3 と回り、i = 2 のときも j = 1, 2, 3 と回り、i = 3 のときも j = 1, 2, 3 と回ります。合計 3 x 3 = 9 回、内側の処理が走るわけです。
ネストループの考え方
二重ループの動きを表にまとめると次のとおりです。
外側 i | 内側 j の動き |
|---|---|
| 1 | 1 → 2 → 3 |
| 2 | 1 → 2 → 3 |
| 3 | 1 → 2 → 3 |
外側 1 回 = 内側 N 回、というのがネストループの基本パターンです。三重ループなら 外側 1 回 = 中側 N 回 = 内側 N x N 回、というふうに掛け算で回数が増えていきます。
二次元配列を作る
九九を二次元配列にしてみましょう。2D array (配列の配列) は次のように作れます。
JavaScript
function makeTable(n) {
const table = [];
for (let i = 1; i <= n; i++) {
const row = [];
for (let j = 1; j <= n; j++) {
row.push(i * j);
}
table.push(row);
}
return table;
}makeTable(3) を呼ぶと、戻り値は次のとおりです。
JavaScript
[
[1, 2, 3],
[2, 4, 6],
[3, 6, 9]
]ポイントは次のとおりです。
- 外側のループの 冒頭で空配列
row = []を作る - 内側のループで
row.push(i * j)していく - 内側のループが終わったら、外側のループで
table.push(row)で行を追加
この「内側の結果を外側で集める」というパターンは、ネストループの典型的な書き方です。木構造を作るときも、グリッド処理をするときも、ほぼ同じ骨格で書けます。
ループ変数は i, j, k の順
ネストループのループ変数は慣習的に 外側から i, j, k という順で名前を付けます。これは Java でも Python でも C でも共通の文化です。i は index の頭文字で、j k はその次のアルファベットというだけの理由ですが、世界中のコードがこの規約を採用しているため、読み手は瞬時に「あ、二重ループだな」と理解できます。
「
ijkだと意味が分からないからrowcolのような名前にしたほうがいいのでは?」と思う人もいるでしょう。実際、グリッド処理など意図がはっきりしている場面ではrowcolのほうが読みやすいです。一方、短い数式処理ではijのほうが視線が散らないという意見もあります。プロジェクトの慣習に合わせるのが無難です。
実行フローを図で追ってみる
二重ループの流れは次の通りです。
外側のループに対して、内側のループが「毎回最初から最後まで」回されているのが矢印で確認できます。
よくある間違い
ネストループを使い始めるとき、初心者がハマる落とし穴が 3 つあります。
- 同じループ変数を使い回す — 内側のループでも
iを使ってしまうと、iが上書きされて外側のループが想定外の動きになります。iとjを分けるのが鉄則です - 外側で
rowを初期化し忘れる — 外側のループの冒頭でconst row = [];を作らないと、前の行の中身が残り続けてしまいます。外側の各回で 新しい空配列 を用意するのを忘れずに - 計算量が爆発する —
O(N^2)(二重) やO(N^3)(三重) はN = 10000になると合計 1 億回や 1 兆回の処理になります。大きな入力ではタイムアウトしやすいので、もっと効率の良いアルゴリズムが無いか考える癖を付けましょう
「ネストループは遅いから避けるべき」と聞くかもしれませんが、九九のような小さな処理では問題ありません。問題は
Nが増えたときだけです。例えば 100 万件の配列を二重ループすると合計 1 兆回になり、現実的な時間で終わりません。N が大きいときだけ気をつける、と覚えておけば OK です。
もうひとつ、ネストループの中で break continue を使うときは要注意です。break は 最も内側のループだけ を抜けるので、外側のループも止めたいときは別のフラグ変数を使うか、関数化して return で抜ける、ラベル文を使うなどの工夫が必要です。
やってみよう
それでは今回の課題に取り組みましょう。やることは次の通りです。
multiplicationTable(n)関数の中身を埋める- 二重ループを使って、
n x nの 掛算表 を二次元配列で返す multiplicationTable(3)は[[1, 2, 3], [2, 4, 6], [3, 6, 9]]、multiplicationTable(1)は[[1]]を返す
ヒントとして、外側のループで i を 1 から n まで回し、その中で const row = []; を作り、内側のループで j を 1 から n まで回して row.push(i * j) します。内側が終わったら table.push(row)、外側が終わったら return table; の流れです。
余裕があれば、Array.from と map を組み合わせた書き方も試してみてください。
JavaScript
const table = Array.from({ length: n }, (_, i) =>
Array.from({ length: n }, (_, j) => (i + 1) * (j + 1))
);これもよく見るパターンです。ネストループだけでなく、関数型スタイルも引き出しに加えると便利です。
この先のレッスンでは素数列挙のような少し応用的なループを学びます。ネストループの基本がしっかり身についていれば、応用問題も怖くないので、ぜひ手を動かして覚えましょう。
よくある質問
Q. ブラウザと Node.js で動作は変わりますか?
A. JavaScript の構文・組み込み機能(配列・文字列・Promise 等)はどちらでも同じです。違いは DOM API(ブラウザ専用)と fs などのファイル系(Node 専用)など、実行環境固有の機能です。コアロジックは両方で動くため、テストもしやすくなります。
Q. ES6 以降の新機能は使っても大丈夫ですか?
A. 現代のブラウザ・Node.js は ES2020 以降の機能をほぼサポートしています。古い IE 等を対象にする必要が無ければ、let/const、アロー関数、分割代入、async/await を積極的に使ってください。トランスパイル(Babel/Vite)が必要なケースは年々減っています。
Q. TypeScript に進む価値はありますか?
A. 型情報があるとリファクタやエディタ補完が劇的に効くため、規模が大きくなるなら TypeScript への移行を強くおすすめします。最初は any を許容しつつ徐々に型を付ければ、JS の知識をそのまま活かせます。
次のレッスン
次は n 以下の素数を列挙する で、ループの中にループを書く「ネストループ」を学び、九九の二次元配列を作ってみよう を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- ネストループ の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. ネストループ とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- 関数名は
multiplicationTable、引数はn1 つ、戻り値は二次元配列 - 二重ループ (ネストループ) を使って実装すること
- 外側のループは行、内側のループは列を表し、
i x jの値を格納すること
入出力例
test-cases.txt
multiplicationTable(3) → [[1,2,3],[2,4,6],[3,6,9]]
multiplicationTable(1) → [[1]]
multiplicationTable(2) → [[1,2],[2,4]]
multiplicationTable(4) → [[1,2,3,4],[2,4,6,8],[3,6,9,12],[4,8,12,16]]