JavaScript 入門

JavaScript 配列のループ

配列のループ処理(イテレーション)は、JavaScriptにおける基本中の基本となるスキルです。配列内の各要素にアクセスして操作することで、データの処理や変換といった高度な実装が可能になります。

本章では、基本的なループ構造の理解をさらに深め、JavaScriptで配列を走査する際に最も頻繁に使われ、かつ有用な手法について解説します。

1. for ループ:クラシックかつコントロール可能

反復プロセスを精密に制御する必要がある場合、標準的な for ループ は非常に強力なツールとなります。特に、各要素の インデックス(index) を知る必要がある場合に重宝します。

1.1 基本的な for ループの構造

for ループの構造は、以下の3つのパートで構成されています。

  • 初期化 (Initialization): カウンタ変数(通常は i)を宣言し、初期化します。
  • 条件 (Condition): ループを継続するかどうかを決定するブーリアン式です。条件が真(true)である限り、ループは続きます。
  • 増分/減分 (Increment/Decrement): 各イテレーションの後にカウンタ変数を更新します。

配列を走査するための for ループの実装例:

const myArray = ["apple", "banana", "cherry"];

for (let i = 0; i < myArray.length; i++) {
  // i は現在の要素のインデックス
  console.log("インデックス " + i + " の要素は: " + myArray[i]);
}

この例の仕組み:

  1. let i = 0; は、カウンタ i を配列の最初の要素のインデックスである 0 に初期化します。
  2. i < myArray.length; は、i が配列の長さよりも小さいかどうかをチェックする条件です。
  3. i++ は、各イテレーションの後に i1 ずつ増やします。
  4. myArray[i] で、現在のインデックス i にある要素にアクセスします。

1.2 例:for ループによる配列要素の修正

for ループを使用して、配列内の要素を直接書き換えることもできます。

const numbers = [1, 2, 3, 4, 5];

for (let i = 0; i < numbers.length; i++) {
  numbers[i] = numbers[i] * 2; // 各要素を 2 倍にする
}

console.log(numbers); // 出力: [2, 4, 6, 8, 10]

1.3 例:逆方向のループ

for ループを使えば、配列を後ろから前へと走査することも簡単です。

const myArray = ["apple", "banana", "cherry"];

for (let i = myArray.length - 1; i >= 0; i--) {
  console.log("インデックス " + i + " の要素は: " + myArray[i]);
}

ここでは、ループは配列の最後のインデックス (myArray.length - 1) から開始され、i が 0 になるまで減分されます。

2. for...of ループ:簡潔かつモダン

for...of ループ は、配列のようなイテラブルなオブジェクトの値を走査するための、より簡潔で可読性の高い手法を提供します。インデックスを管理することなく、各要素に直接アクセスできるのが特徴です。

2.1 基本的な for...of の構造

const myArray = ["apple", "banana", "cherry"];

for (const element of myArray) {
  console.log(element);
}

この例の仕組み:

  • const element of myArraymyArray 内の各要素を自動的に取り出します。
  • 各イテレーションにおいて、現在の要素の値が変数 element に代入されます。

2.2 例:条件文との組み合わせ

for...of ループ内に条件分岐を組み込むことで、要素の値に応じた特定の処理を実行できます。

const numbers = [1, 2, 3, 4, 5, 6];

for (const number of numbers) {
  if (number % 2 === 0) {
    console.log(number + " は偶数です");
  } else {
    console.log(number + " は奇数です");
  }
}

3. for...in ループ(配列への使用には注意が必要)

for...in ループ は、本来オブジェクトの列挙可能なプロパティ(キー)を走査するために設計されたものです。配列に対しても使用可能ですが、配列のループ処理においては通常推奨されません。

これは、for...in が配列の「値」ではなく「キー (Keys)」(すなわちインデックス)を走査するためであり、また継承されたプロパティまで含んでしまう可能性があるため、予期せぬ挙動を引き起こすことがあります。

3.1 for...in の構造(なぜ配列のループに避けるべきか)

const myArray = ["apple", "banana", "cherry"];

for (const index in myArray) {
  console.log("インデックス: " + index + ", 値: " + myArray[index]);
}

一見、普通のループに見えますが、以下のデメリットがあります:

  1. 走査されるのはプロパティ名(文字列): インデックス ("0", "1", "2") は数値ではなく文字列タイプとして扱われます。これにより、数学的な計算を行う際に意図しない結果を招くことがあります。
  2. 継承されたプロパティを含む: もし Array.prototype にカスタムプロパティを追加していた場合、for...in ループはそれらも走査対象に含めてしまいます。
  3. 順序が保証されない: プロパティを走査する順序は、定義された順序と必ずしも一致するとは限りません(モダンなブラウザでは概ね一致しますが、仕様上保証されていません)。

まとめ: 配列のループ処理には for...in を避け、for または for...of を使用するようにしましょう。

3.2 for...in が有効なケース(オブジェクト)

for...in ループは、主にオブジェクト(Object)のプロパティを走査するために使用します。

const myObject = {
  name: "John",
  age: 30,
  city: "New York"
};

for (const key in myObject) {
  console.log("キー: " + key + ", 値: " + myObject[key]);
}

この場合、for...inmyObject のキーである "name"、"age"、"city" を順に走査します。

4. ループの制御:break と continue

これまでに学んだ breakcontinue ステートメントは、配列のループ内でも非常に有効に機能します。

4.1 break ステートメント

break ステートメントは、ループを完全に終了させます。「検索」のシナリオにおいて、目的のデータが見つかった瞬間にループを止めるのに最適です。

const numbers = [1, 2, 3, 4, 5, 6];

for (const number of numbers) {
  if (number > 3) {
    break; // 数字が 3 より大きくなった時点でループを抜ける
  }
  console.log(number);
}
// 出力:
// 1
// 2
// 3

4.2 continue ステートメント

continue ステートメントは、現在のイテレーションの残りの処理をスキップし、即座に次のイテレーションへ移ります。「フィルタリング」のシナリオで役立ちます。

const numbers = [1, 2, 3, 4, 5, 6];

for (const number of numbers) {
  if (number % 2 === 0) {
    continue; // 偶数の場合はスキップして次の数字へ
  }
  console.log(number);
}
// 出力:
// 1
// 3
// 5