JavaScript 入門

JavaScript ネストされたループ

ネスト(入れ子)されたループを使用すると、あるループの内部で別のループを繰り返し実行することができます。

これは、多次元データの処理、特定のパターンの生成、あるいは複数の要素セットを組み合わせて走査する必要がある複雑な問題を解決するための強力な手法です。

1. ネストされたループを理解する

ネストされたループとは、単に別のループの内部に配置されたループのことです。外側のループが1回実行(イテレーション)されるたびに、内側のループは最初から最後まで完全に実行されます。

1.1 基本構造

ネストされたループの一般的な構造は以下の通りです:

for (let i = 0; i < outerLimit; i++) { // アウターループ(外側)
  for (let j = 0; j < innerLimit; j++) { // インナーループ(内側)
    // インナーループの各イテレーションで実行されるコード
    // このコードは合計で (outerLimit * innerLimit) 回実行されます
  }
}
  • アウターループ (Outer Loop): インナーループを包んでいるループです。インナーループを何セット実行するかを制御します。
  • インナーループ (Inner Loop): アウターループの内部にネストされているループです。アウターループが1回イテレーションするたびに、このループは最初から最後まで完走します。
  • イテレーション回数: インナーループ内のコードが実行される総回数は、アウターループの回数 x インナーループの回数となります。

1.2 動作確認:シンプルなネストされた for ループ

ネストされたループがどのように動作するかを、簡単な例で見てみましょう:

for (let i = 0; i < 3; i++) { // アウターループ: i = 0, 1, 2
  for (let j = 0; j < 2; j++) { // インナーループ: j = 0, 1
    console.log(`i = ${i}, j = ${j}`);
  }
}

出力:

i = 0, j = 0
i = 0, j = 1
i = 1, j = 0
i = 1, j = 1
i = 2, j = 0
i = 2, j = 1

この例では、アウターループが3回(i = 0, 1, 2)実行されます。アウターループの各イテレーションごとに、インナーループが2回(j = 0, 1)実行されます。その結果、console.log は合計で 3 x 2 = 6 回実行されることになります。

1.3 ネストされた while ループ

同様の方法で while ループをネストさせることもできます。

let i = 0;
while (i < 3) { // アウターループ
  let j = 0;
  while (j < 2) { // インナーループ
    console.log(`i = ${i}, j = ${j}`);
    j++;
  }
  i++;
}

このコードは、先ほどの for ループの例と同じ出力を生成します。主な違いは、ループカウンタ(i と j)を手動で初期化し、インクリメント(増加)させる必要がある点です。

1.4 異なる種類のループを組み合わせる

異なる種類のループを互いにネストさせることも可能です。

let i = 0;
while (i < 3) { // アウター while ループ
  for (let j = 0; j < 2; j++) { // インナー for ループ
    console.log(`i = ${i}, j = ${j}`);
  }
  i++;
}

この例では、アウターに while、インナーに for を使用しています。ロジックは変わりません:アウターループが1回実行されるたびに、インナーループが最初から最後まで完全に実行されます。

2. 実践的な例とデモンストレーション

ネストされたループは、多くの実用的なシナリオで役立ちます。

2.1 2次元配列(行列)の生成

ネストされたループは、2次元配列(行列 / マトリックス)を作成したり操作したりする際によく使われます。

// すべての要素を 0 で埋めた 3x3 の行列を作成
const matrix = [];

for (let i = 0; i < 3; i++) {
  matrix[i] = []; // 各行を初期化
  for (let j = 0; j < 3; j++) {
    matrix[i][j] = 0; // 各要素に 0 を代入
  }
}

console.log(matrix);
/* 出力:
[
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
]
*/

ここでは、アウターループが行を走査し、インナーループが列を走査しています。各行に対して新しい配列を作成し、そこに 0 を格納していくことで行列を構築しています。

2.2 九九の表(乗算表)の作成

ネストされたループを使って、九九の表(1から5まで)を生成してみましょう。

// 1 から 5 までの乗算表を生成
for (let i = 1; i <= 5; i++) { // アウターループ: 行
  let row = "";
  for (let j = 1; j <= 5; j++) { // インナーループ: 列
    row += (i * j) + "\t"; // 積を計算し、タブ文字を追加
  }
  console.log(row); // その行をプリント
}

出力:

1       2       3       4       5
2       4       6       8       10
3       6       9       12      15
4       8       12      16      20
5       10      15      20      25

アウターループが「段」を制御し、インナーループがその段の中での「掛け算」を実行しています。

2.3 2次元配列内での検索

ネストされたループは、2次元配列の中から特定の要素を探し出す際にも使用されます。

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
const target = 5;
let found = false;

for (let i = 0; i < matrix.length; i++) {
  for (let j = 0; j < matrix[i].length; j++) {
    if (matrix[i][j] === target) {
      console.log(`matrix[${i}][${j}] で ${target} を発見しました`);
      found = true;
      break; // 発見したのでインナーループを抜ける
    }
  }
  if (found) {
    break; // 発見したのでアウターループも抜ける
  }
}

if (!found) {
  console.log(`${target} は行列内に見つかりませんでした`);
}

この例では、要素が見つかった瞬間に break 文を使用して両方のループを終了させています。これにより、不要な計算を避けることができます。

2.4 2つの配列間の要素を比較する

異なる2つの配列を比較して、共通の要素を見つける際にもネストされたループが役立ちます。

const array1 = [1, 2, 3, 4, 5];
const array2 = [3, 5, 7, 9];

for (let i = 0; i < array1.length; i++) {
  for (let j = 0; j < array2.length; j++) {
    if (array1[i] === array2[j]) {
      console.log(`共通要素を発見: ${array1[i]}`);
    }
  }
}

このコードは array1 の各要素に対して、array2 のすべての要素を一つずつ照らし合わせ、一致するものがあればコンソールに出力します。