JavaScriptでのforEachとindexの使い分け方法12選

JavaScriptのforEachとindexを使った初心者向けの徹底解説画像JS
この記事は約16分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●forEachの基本的な使い方

JavaScriptでループ処理を行う際、for文と並んでよく使われるのがforEachメソッドです。

forEachは配列の各要素に対して、指定したコールバック関数を実行するメソッドで、シンプルで読みやすいコードを書くことができます。

配列に対するforEachの構文は、次のようになります。

array.forEach(function(element, index, array) {
  // 処理内容
});

第1引数には、現在処理されている要素の値が渡されます。

第2引数はオプションで、現在の要素のインデックスが渡されます。

第3引数も同様にオプションで、forEachを呼び出した配列自体が渡されます。

実際にforEachを使ってみましょう。

まずは配列の各要素を出力する簡単なサンプルコードです。

○サンプルコード1:配列の各要素を出力する

const fruits = ['apple', 'banana', 'orange'];

fruits.forEach(function(fruit) {
  console.log(fruit);
});

実行結果

apple
banana
orange

このように、forEachを使えば配列の要素を1つずつ取り出して処理を行うことができます。

コールバック関数には、アロー関数を使ってより簡潔に書くこともできますね。

○サンプルコード2:indexを使って配列要素にアクセス

先ほどの例では、要素の値のみを使いましたが、インデックスを使って配列の要素にアクセスすることもできます。

const fruits = ['apple', 'banana', 'orange'];

fruits.forEach(function(fruit, index) {
  console.log(`${index}: ${fruit}`);
});

実行結果

0: apple
1: banana
2: orange

forEachの第2引数で受け取ったindexを使えば、配列のインデックスを取得できます。

●forEachとforの違い

JavaScriptでループ処理を行う際、forEachとforはどちらもよく使われる構文ですが、それぞれ特徴があります。

実行速度と可読性の観点から、両者の違いを見ていきましょう。

一般的に、実行速度はforループの方が速いと言われています。

forループは単純な構文で、条件式と増分式を明示的に書くため、ループの制御がしやすいからです。

一方、forEachはコールバック関数を呼び出すため、関数呼び出しのオーバーヘッドが発生します。

実際に、大量の要素を持つ配列に対してループ処理を行った場合、forループの方が数倍速いというベンチマーク結果もあります。

しかし、コードの可読性や保守性を考えると、forEachを使った方が良いケースも多いのです。

○サンプルコード3:forループで配列を処理

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

for (let i = 0; i < numbers.length; i++) {
  console.log(numbers[i]);
}

forループでは、カウンター変数iを初期化し、配列の長さ未満である限りループを続けます。

配列の要素にアクセスするには、numbers[i]のようにインデックスを使う必要があります。

一方、forEachを使えば、配列の要素を直接取得できるため、コードがシンプルで読みやすくなります。

○サンプルコード4:forEachでのインデックス取得

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

numbers.forEach((number, index) => {
  console.log(`${index}: ${number}`);
});

forEachのコールバック関数では、要素の値とインデックスを引数で受け取れます。

アロー関数を使えば、さらに簡潔に書けますね。

forEachを使うと、配列の要素に直接アクセスできるため、コードの意図が明確になります。

ループのカウンター変数や条件式を管理する必要がないので、ループの本質的な処理に集中できるからです。

チームで開発する際、forEachを使ったコードの方が可読性が高く、バグが混入するリスクを減らせます。

新しくプロジェクトに参加したメンバーも、処理の流れを追いやすいでしょう。

したがって、実行速度よりも可読性や保守性を重視する場合は、forEachを使うのがおすすめです。

●連想配列でのforEachの使い方

連想配列、つまりオブジェクトに対してもforEachを使うことができます。

その際、Object.keys()やObject.values()、Object.entries()といったメソッドと組み合わせると、キーや値を柔軟に取得できるようになります。

Object.entries()メソッドを使えば、オブジェクトのキーと値のペアを配列として取得できます。

たとえば、ユーザー情報を格納したオブジェクトがあるとします。

○サンプルコード5:連想配列のキーと値を取得

const user = {
  name: 'John Smith',
  age: 30,
  email: 'john@example.com'
};

Object.entries(user).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

実行結果

name: John Smith
age: 30
email: john@example.com

Object.entries(user)でキーと値のペアを配列で取得し、forEachのコールバック関数でそれぞれを分割代入しています。

これにより、キーと値を個別に扱うことができます。

オブジェクトのプロパティを列挙する場合、for…inループを使うこともできます。

○サンプルコード6:オブジェクトのプロパティを列挙

const user = {
  name: 'John Smith',
  age: 30,
  email: 'john@example.com'
};

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}

for…inループでは、オブジェクトのキーを順番に取得できます。

ただし、プロトタイプチェーン上のプロパティも列挙されてしまうので、必要に応じてhasOwnProperty()メソッドでフィルタリングする必要があります。

さらに、ネストした連想配列を扱う場合は、再帰的な処理が有効です。

○ネストした連想配列の操作

const data = {
  users: [
    { id: 1, name: 'John', age: 30 },
    { id: 2, name: 'Alice', age: 25 },
  ],
  posts: [
    { id: 1, title: 'Hello World', authorId: 1 },
    { id: 2, title: 'Introduction', authorId: 2 },
  ],
};

function printNestedObject(obj) {
  Object.entries(obj).forEach(([key, value]) => {
    console.log(`${key}:`);
    if (Array.isArray(value)) {
      value.forEach((item) => {
        printNestedObject(item);
        console.log('---');
      });
    } else if (typeof value === 'object') {
      printNestedObject(value);
    } else {
      console.log(value);
    }
  });
}

printNestedObject(data);

printNestedObject()関数では、オブジェクトのキーと値を再帰的に処理しています。

値が配列の場合は、さらにその要素を再帰的に処理。オブジェクトの場合も同様です。

これにより、ネストの深さに関係なく、連想配列のすべての要素にアクセスできます。

このように、連想配列でもforEachを活用することで、柔軟なデータ操作が可能になります。

オブジェクトのキーと値を同時に取得したり、ネストした構造を再帰的に処理したりできるからです。

実際の開発では、外部APIから取得したJSONデータを連想配列として扱うことが多いですよね。

そんな時、forEachを使ってデータを加工・整形すれば、アプリケーションで使いやすい形に変換できます。

●アロー関数を使ったforEach

ここからは、モダンなJavaScriptの書き方の代表格であるアロー関数を使ったforEachの使い方について解説していきます。

アロー関数を活用することで、よりシンプルで読みやすいコードを書くことができるようになるでしょう。

アロー関数は、function キーワードを使った従来の関数定義を簡潔に書き換える構文です。

アロー関数を使えば、コールバック関数の定義をワンライナーで書けるため、コードの見通しが良くなります。

従来の関数定義とアロー関数を比べてみましょう。

○アロー関数の基本構文

従来の関数定義

const double = function(x) {
  return x * 2;
};

アロー関数での定義

const double = (x) => {
  return x * 2;
};

さらに、関数の本体が1つの式だけの場合、ブロックと return を省略できます。

const double = (x) => x * 2;

このアロー関数を使って、forEachのコールバック関数をシンプルに書き換えることができます。

○サンプルコード7:シンプルな記述でループ処理

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

numbers.forEach((num) => {
  console.log(num);
});

アロー関数を使えば、コールバック関数の定義がワンライナーで済むので、全体的にスッキリとした印象になります。

さらに、アロー関数を使えば、特定の条件を満たす要素だけを処理するような場合にも、コードが読みやすくなります。

○サンプルコード8:特定の条件を満たす要素の処理

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

numbers.forEach((num) => {
  if (num % 2 === 0) {
    console.log(`${num} is even`);
  } else {
    console.log(`${num} is odd`);
  }
});

偶数と奇数で異なる処理を行う場合でも、アロー関数を使えばif文の中身がすっきりと書けますね。

アロー関数を使ったforEachは、コードの可読性を高めつつ、処理の流れを明確にしてくれます。

無駄な記述が減ることで、ループ処理の本質的な部分に集中できるからです。

チームで開発している時、アロー関数を使ったコードなら、他のメンバーも処理の内容を素早く理解できるはず。

コードレビューの時間も短縮できそうです。

慣れないうちは、従来の関数定義とアロー関数を併用しながら、徐々にアロー関数に移行していくのがおすすめです。

●よくあるエラーと対処法

せっかくforEachを使ってループ処理を書いたのに、エラーが出てしまった…。そんな経験、ありませんか?

実は、forEachを使う際によく遭遇するエラーがいくつかあるんです。ここでは、代表的なエラーとその対処法を見ていきましょう。

「forEach is not a function」というエラーは、forEachを配列ではないオブジェクトに対して使おうとした時に発生します。

forEachは配列に対して使うメソッドなので、配列以外のオブジェクトに使うとエラーになってしまうのです。

例えば、nullやundefinedに対してforEachを使おうとすると、このエラーが発生します。

○「forEach is not a function」エラーの原因と解決策

const data = null;
data.forEach((item) => {
  console.log(item);
});

このコードでは、nullに対してforEachを使おうとしているため、エラーが発生します。

解決策としては、事前にデータが配列であることを確認するのが有効です。

if (Array.isArray(data)) {
  data.forEach((item) => {
    console.log(item);
  });
} else {
  console.log('データが配列ではありません');
}

Array.isArray()メソッドで配列かどうかをチェックすれば、エラーを未然に防げますね。

「Cannot read property ‘forEach’ of undefined」エラーも、似たような原因で発生します。

○「Cannot read property ‘forEach’ of undefined」エラーへの対処

このエラーは、undefinedに対してforEachを使おうとした時に起こります。

let data;
data.forEach((item) => {
  console.log(item);
});

この場合も、データが存在することを確認してからforEachを使うのが賢明です。

if (data) {
  data.forEach((item) => {
    console.log(item);
  });
} else {
  console.log('データが未定義です');
}

データが未定義の場合は、適切なエラーメッセージを表示するなどの処理を行いましょう。

パフォーマンス面での注意点として、forEachはfor文よりも若干処理速度が遅いとされています。

forEachはコールバック関数を呼び出すため、関数呼び出しのオーバーヘッドが発生するからです。

大量のデータを処理する場合、forEachよりもfor文の方が高速に処理できることがあります。

○パフォーマンス面での注意点

const largeArray = [...Array(100000).keys()];

console.time('forEach');
largeArray.forEach((item) => {
  // 処理内容
});
console.timeEnd('forEach');

console.time('for');
for (let i = 0; i < largeArray.length; i++) {
  // 処理内容
}
console.timeEnd('for');

このようにパフォーマンスを比較すると、forEachよりもfor文の方が若干高速なことがわかります。

ただし、パフォーマンスの差は多くの場合微々たるものなので、可読性を優先してforEachを使うのがおすすめです。

●forEachの応用例

ここまで、forEachの基本的な使い方から、連想配列での活用法、アロー関数との組み合わせまで見てきました。

最後に、実務でよく使われるforEachの応用例をいくつか紹介しましょう。

forEachを使えば、配列の各要素に対して同じ処理を行うことができます。

例えば、配列の合計値を求めるような場合です。

○サンプルコード9:配列の合計値を求める

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

numbers.forEach((num) => {
  sum += num;
});

console.log(sum); // 出力結果: 15

forEachを使って配列の各要素を順番に処理し、合計値を求めています。

変数sumを宣言し、コールバック関数内で各要素を加算していくことで、配列の合計値が得られます。

複数の配列を組み合わせて処理する際にも、forEachが活躍します。

○サンプルコード10:複数の配列を組み合わせて処理

const names = ['Alice', 'Bob', 'Charlie'];
const ages = [24, 30, 28];

names.forEach((name, index) => {
  console.log(`${name} is ${ages[index]} years old.`);
});

実行結果

Alice is 24 years old.
Bob is 30 years old.
Charlie is 28 years old.

namesとagesという2つの配列を組み合わせて処理しています。

forEachのコールバック関数では、第2引数でインデックスを受け取れるため、それを使ってagesの対応する要素にアクセスしています。

さらに、DOMの要素を操作する際にもforEachが使えます。

○サンプルコード11:DOMの要素を操作

const items = document.querySelectorAll('ul li');

items.forEach((item) => {
  item.addEventListener('click', () => {
    item.classList.toggle('completed');
  });
});

querySelectorAll()で取得したDOMの要素に対して、forEachを使ってクリックイベントを追加しています。

これにより、リストの各アイテムをクリックすると、completedクラスが付与または削除されるようになります。

非同期処理との組み合わせも、forEachの得意分野です。

○サンプルコード12:非同期処理との組み合わせ

const urls = [
  'https://api.example.com/data1',
  'https://api.example.com/data2',
  'https://api.example.com/data3',
];

urls.forEach(async (url) => {
  const response = await fetch(url);
  const data = await response.json();
  console.log(data);
});

URLの配列に対してforEachを使い、非同期でデータを取得しています。

async/awaitを使うことで、各URLに対するリクエストを順番に処理できます。

このように、forEachは配列の操作だけでなく、様々な場面で活用できる万能なメソッドなのです。

シンプルな記述で配列の各要素に対して処理を行えるからこそ、多様な応用が可能になります。

実際の開発では、ユーザーの入力データを検証したり、APIから取得したデータを加工したりと、配列の処理が欠かせません。

そんな時、forEachを使えば、コードの見通しを保ちつつ、効率的に処理を記述できるでしょう。

まずはシンプルな例から始めて、徐々に応用範囲を広げていくのが上達のコツです。

まとめ

今回は、JavaScriptのforEachとindexの使い分け方について、基本的な使い方から応用例まで幅広く解説してきました。

forEachはシンプルな記述で配列の各要素に対して処理を行えるため、様々な場面で活躍する万能なメソッドです。

一方、従来のforループと比べると実行速度は若干劣りますが、コードの可読性と保守性を重視する場合はforEachを使うのがおすすめです。

アロー関数と組み合わせることで、よりシンプルで読みやすいコードを書くことができます。

連想配列の操作や、DOMの要素の操作、非同期処理との組み合わせなど、forEachの応用範囲は広いので、ぜひ様々なケースで活用してみてください。

エラー対策にも気を付けながら、JavaScriptプログラミングを楽しんでいきましょう!