入れ子のループで掛算表を作る

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

入れ子のループで掛算表を作る とは

ループの中にループを書く「ネストループ」を学び、九九の二次元配列を作ってみよう。本レッスンでは、入れ子のループで掛算表を作る の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

ループの中にループを書く「ネストループ」

ここまで学んだループは「配列を 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 の動き
11 → 2 → 3
21 → 2 → 3
31 → 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 でも共通の文化です。iindex の頭文字で、j k はその次のアルファベットというだけの理由ですが、世界中のコードがこの規約を採用しているため、読み手は瞬時に「あ、二重ループだな」と理解できます。

i j k だと意味が分からないから row col のような名前にしたほうがいいのでは?」と思う人もいるでしょう。実際、グリッド処理など意図がはっきりしている場面では row col のほうが読みやすいです。一方、短い数式処理では i j のほうが視線が散らないという意見もあります。プロジェクトの慣習に合わせるのが無難です。

実行フローを図で追ってみる

二重ループの流れは次の通りです。

diagram (will load when visible)

外側のループに対して、内側のループが「毎回最初から最後まで」回されているのが矢印で確認できます。

よくある間違い

ネストループを使い始めるとき、初心者がハマる落とし穴が 3 つあります。

  • 同じループ変数を使い回す — 内側のループでも i を使ってしまうと、i が上書きされて外側のループが想定外の動きになります。ij を分けるのが鉄則です
  • 外側で row を初期化し忘れる — 外側のループの冒頭で const row = []; を作らないと、前の行の中身が残り続けてしまいます。外側の各回で 新しい空配列 を用意するのを忘れずに
  • 計算量が爆発するO(N^2) (二重) や O(N^3) (三重) は N = 10000 になると合計 1 億回や 1 兆回の処理になります。大きな入力ではタイムアウトしやすいので、もっと効率の良いアルゴリズムが無いか考える癖を付けましょう

「ネストループは遅いから避けるべき」と聞くかもしれませんが、九九のような小さな処理では問題ありません。問題は N が増えたときだけです。例えば 100 万件の配列を二重ループすると合計 1 兆回になり、現実的な時間で終わりません。N が大きいときだけ気をつける、と覚えておけば OK です。

もうひとつ、ネストループの中で break continue を使うときは要注意です。break最も内側のループだけ を抜けるので、外側のループも止めたいときは別のフラグ変数を使うか、関数化して return で抜ける、ラベル文を使うなどの工夫が必要です。

やってみよう

それでは今回の課題に取り組みましょう。やることは次の通りです。

  1. multiplicationTable(n) 関数の中身を埋める
  2. 二重ループを使って、n x n掛算表 を二次元配列で返す
  3. multiplicationTable(3)[[1, 2, 3], [2, 4, 6], [3, 6, 9]]multiplicationTable(1)[[1]] を返す

ヒントとして、外側のループで i1 から n まで回し、その中で const row = []; を作り、内側のループで j1 から n まで回して row.push(i * j) します。内側が終わったら table.push(row)、外側が終わったら return table; の流れです。

余裕があれば、Array.frommap を組み合わせた書き方も試してみてください。

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 つができることを確認しましょう。

  1. ネストループ の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. ネストループ とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 関数名は multiplicationTable、引数は n 1 つ、戻り値は二次元配列
  2. 二重ループ (ネストループ) を使って実装すること
  3. 外側のループは行、内側のループは列を表し、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]]

ヒント

main.js
main.js
学習モード

メモ

入れ子のループで掛算表を作る

⌘S で保存