🖐️

そのfor文ちょっと待った!! ループ処理の代替表現ガイド

2024/01/31に公開

なぜfor文が良くない場合があるのか

プログラミングにおけるfor文は、繰り返し処理の便利さから、広く使用されています。
しかし、この便利なメソッドにはいくつかの難点があり、扱いが難しい側面があります。

1. 可読性の低下

for文は特に、ネストされたループや複数の条件を含む場合、コードの可読性を低下させることがあります。
例えば、以下のコードは二次元配列の各要素を2倍にする処理を行っていますが、二重のfor文により、何をしているのか一見して理解しづらくなっています。

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

let doubledMatrix = [];
for (let i = 0; i < matrix.length; i++) {
  let row = matrix[i];
  let doubledRow = [];
  for (let j = 0; j < row.length; j++) {
    doubledRow.push(row[j] * 2);
  }
  doubledMatrix.push(doubledRow);
}

console.log(doubledMatrix); // [[2, 4, 6], [8, 10, 12], [14, 16, 18]]

この例では、各ループのインデックス(iとj)の管理が複雑になっています。
これは、特に他の開発者がコードを引き継いだ際に、解読に時間がかかり、生産性の低下につながる可能性があります。

2. バグの発生しやすさ

for文は、特にインデックスの管理や終了条件の設定において、バグを引き起こしやすい構造を持っています。インデックスのオフバイワンエラーや無限ループといった問題は、for文において頻繁に発生する典型的なバグです。これらのエラーは見つけにくく、時には深刻な問題を引き起こすこともあります。
例えば、以下のコードでは、オフバイワンエラーが発生し、配列の範囲外参照の問題を引き起こしています。

const array = [1, 2, 3, 4, 5];
for (let i = 0; i <= array.length; i++) {
  // オフバイワンエラー:iはarray.length (5)まで実行されるが、配列の最大インデックスは4
}

この例では、ループの終了条件がi <= array.lengthとなっているため、iが配列の最後のインデックスを超えてしまい、存在しない要素にアクセスしようとします。これは、配列の範囲外参照につながり、予期しない挙動やエラーを引き起こす可能性があります。

for文の代替処理ケース集

以下では、JavaScriptとTypeScriptの両方で使用できる代替手段を紹介します。
これらの記述を採用することで、コードをよりシンプルにし、意図を明確にさせることができるだけでなく、バグの発生率を下げ、生産性を向上させる助けとなるかもしれません。

1. 配列の各要素に対する操作

for文の使用例:

for (let i = 0; i < array.length; i++) {
  // 配列の各要素に対する何らかの操作
}

代替方法:forEach()

array.forEach(element => {
  // 各要素に対する操作
});

実践例:ユーザーのリストを処理し、各ユーザーのデータを表示する。

const users = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];
// for文を使った例
for (let i = 0; i < users.length; i++) {
  console.log(users[i].name);
}
// forEach()を使った例
users.forEach(user => console.log(user.name));

forEach()を使用することで、各ユーザーの名前をシンプルに表示できます。
これにより、コードの可読性が向上し、エラーの可能性が減少します。

2. 配列の要素を変換して新しい配列を作成

for文の使用例:

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

代替方法:map()

const newArray = array.map(element => element * 2);

実践例:製品の価格リストを取得し、税込み価格のリストを作成する。

const prices = [100, 200, 300];
// for文を使った例
let taxedPrices = [];
for (let i = 0; i < prices.length; i++) {
  taxedPrices.push(prices[i] * 1.1);
}
// map()を使った例
const taxedPrices = prices.map(price => price * 1.1);

map()を使用することで、税込み価格の新しい配列を効率的に作成できます。
コードが短くなり、意図が明確になります。

3. 配列の要素を集約して単一の値を計算

for文の使用例:

let sum = 0;
for (let i = 0; i < array.length; i++) {
  sum += array[i]; // 例:合計の計算
}

代替方法:reduce()

const sum = array.reduce((accumulator, currentValue) => accumulator + currentValue, 0);

実践例:オンラインストアのカート内の商品の合計価格を計算する。

const cartItems = [100, 200, 300];
// for文を使った例
let total = 0;
for (let i = 0; i < cartItems.length; i++) {
  total += cartItems[i];
}
// reduce()を使った例
const total = cartItems.reduce((sum, item) => sum + item, 0);

reduce()を使用することで、カート内の商品の合計価格を簡単に計算できます。
コードがより簡潔になり、エラーのリスクが減ります。

4. 条件に基づく要素の抽出

for文の使用例:

let filteredArray = [];
for (let i = 0; i < array.length; i++) {
  if (array[i] > 5) { // 例:特定の条件に一致する要素を抽出
    filteredArray.push(array[i]);
  }
}

代替方法:filter()

const filteredArray = array.filter(element => element > 5);

実践例:特定の価格以上の製品だけをリストアップする。

const products = [{ name: 'Book', price: 1200 }, { name: 'Pen', price: 500 }, { name: 'Bag', price: 2000 }];
// for文を使った例
let expensiveProducts = [];
for (let i = 0; i < products.length; i++) {
  if (products[i].price > 1500) {
    expensiveProducts.push(products[i]);
  }
}
// filter()を使った例
const expensiveProducts = products.filter(product => product.price > 1500);

filter()メソッドを使うことで、条件に合致する要素だけを簡単に新しい配列に抽出できます。
これにより、コードが短く、意図がより明確になります。

5. オブジェクトのプロパティに対する反復処理

for文の使用例:

for (let key in object) {
  // オブジェクトの各プロパティに対する操作
}

代替方法:Object.keys(), Object.values(), Object.entries()

Object.keys(object).forEach(key => {
  // キーに対する操作
});

Object.values(object).forEach(value => {
  // 値に対する操作
});

Object.entries(object).forEach(([key, value]) => {
  // キーと値に対する操作
});

実践例:ユーザーのプロフィールオブジェクトから特定の情報を表示する。

const userProfile = { name: 'Alice', age: 25, email: 'alice@example.com' };
// for文を使った例
for (let key in userProfile) {
  console.log(`${key}: ${userProfile[key]}`);
}
// Object.entries()を使った例
Object.entries(userProfile).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});
// ログ結果:
// name: Alice
// age: 25
// email: alice@example.com

Object.entries()を使うことで、オブジェクトのキーと値のペアを簡単に反復処理できます。
これにより、コードがより簡潔で読みやすくなるかもしれません。

さいごに

この記事では、for文の代替として、forEach()やmap()、reduce()などを紹介しました。これらのメソッドは、コードをすっきりとして読みやすくし、時に、エラーの可能性を減少させてくれます。また、パフォーマンスの観点からも、効率的となり得ます。
しかし、すべての場合においてfor文を置き換えるわけではなく、選択肢の幅をもつこと、状況に応じて最適な方法を選ぶことが大切だと思います。

わかりやすいコードは、他の人が読んで理解しやすく、チーム開発をスムーズに進めるための重要な手段です。より明確で、シンプル、そして生産的なコーディングを目指していきたいものですね。

LCL Engineers

Discussion