Skip to content
Playground

8.ネストしたループ

学習目標

  • ループの中にループを書いて、2重の繰り返し処理を実行できる
  • ネストしたループの実行順序をトレースできる

「1 から 3 までの数の組み合わせをすべて出力する」プログラムを考えます。(1,1), (1,2), (1,3), (2,1), … , (3,3) の 9 通りです。

1つの for ループで扱える変数は1つだけです。2つの変数を組み合わせるには、ループの中にもう1つのループを入れます。これを ネストしたループ(入れ子のループ)と呼びます。

nested-loops.js
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
console.log(i + "," + j);
}
}
1,1
1,2
1,3
2,1
2,2
2,3
3,1
3,2
3,3

外側のループでは i が 1, 2, 3 と進みます。i が 1 つの値を取っているあいだに、内側のループで j が 1, 2, 3 と進みます。内側のループが終わると外側に戻り、i が次の値になって、内側のループがまた j = 1 から始まります。

外側と内側の変数がどう進むかを 1 ステップずつ追います。

ij出力
111,1
121,2
131,3
212,1
222,2
232,3
313,1
323,2
333,3

i の値ごとに j が 3 回動くので、全部で 3 × 3 = 9 回の出力になります。

ネストしたループの典型的な例が九九の表です。

nested-loops.js
for (let i = 1; i <= 9; i++) {
let line = "";
for (let j = 1; j <= 9; j++) {
line = line + (i * j) + "\t";
}
console.log(line);
}
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81

外側の i が段(行)、内側の j が列を表しています。"\t" はタブ文字で、値の間にスペースを空けるために使っています。

let line = "" を外側のループの中に書くことで、行ごとに空文字列から始めます。外側のループが 1 周するたびに line がリセットされ、1 行分の文字列が新しく組み立てられます。

i = 1 のとき、内側のループで line がどう変化するかを追います。

ji * jline
11"1\t"
22"1\t2\t"
33"1\t2\t3\t"
99"1\t2\t3\t4\t5\t6\t7\t8\t9\t"

内側のループが終わった時点で、console.log(line) が 1 行分をまとめて出力します。外側のループの次の周回では、line が再び空文字列にリセットされ、2 行目が組み立てられます。

ネストしたループは、配列の要素同士をすべての組み合わせで比較するときにも使います。この「すべてのペアを比較する」パターンは ソートアルゴリズム でも使います。

nested-loops.js
const data = [3, 1, 4];
for (let i = 0; i < data.length; i++) {
for (let j = i + 1; j < data.length; j++) {
console.log(data[i] + "" + data[j]);
}
}
3 と 1
3 と 4
1 と 4

出力されるペアは (3, 1), (3, 4), (1, 4) の 3 通りです。自分自身との比較や、順序を入れ替えただけのペアは含まれません。

内側のループが j = i + 1 から始まっているのがポイントです。j = 0 から始めると、同じペアが 2 回出力されたり、自分自身と比較したりしてしまいます。j = i + 1 にすることで、まだ比較していないペアだけを処理します。

i について、内側のループがどの j を取るかをトレースします。

idata[i]j が取る値出力されるペア
031, 23 と 1, 3 と 4
1121 と 4
24(なし)

i = 2 のとき、内側のループの初期値は j = i + 1 = 3 ですが、条件式 j < data.length3 < 3false になり、内側のループは 1 度も実行されません。配列の最後の要素には、比較相手が残っていないためです。