JavaScript | 配列・オブジェクト:ループ処理 – ループの可読性

JavaScript JavaScript
スポンサーリンク

ループの可読性とは何か

「可読性が高いループ」とは、意図が一目で分かり、バグが起きにくく、後から変更しやすい書き方です。ここが重要です:処理の目的(抽出・変換・集計・実行)に合った構文を選び、否定条件を早く捨て、インデックス管理を必要最小に抑え、非破壊の原則を守ることで、ループはぐっと読みやすくなります。


目的別に選ぶ構文

役割に合う道具を選ぶ

  • 変換(新しい配列が欲しい): map
  • 抽出(条件で残す): filter
  • 集計(ひとつに畳み込む): reduce
  • 副作用の実行(ログ・DOM 操作): forEach
  • 柔軟な制御(break/continue/await): for…of/for

意図に合う構文を選ぶと、ループ内部の分岐や手続きが減り、コードが目的中心になります。

例:意図が伝わる置き換え

// 目的が「在庫ありのラベル作成」なら…
const labels = products
  .filter(p => p.stock > 0)            // 抽出
  .map(p => `${p.name} (${p.price})`); // 変換
JavaScript

ネストを浅く保つテクニック

否定条件は先に捨てる

for (const p of products) {
  if (!p) continue;           // 欠損はスキップ
  if (p.stock <= 0) continue; // 在庫なしはスキップ
  // 核心だけを書く
  labels.push(`${p.name} (${p.price})`);
}
JavaScript
  • ガードで浅く: 早めの continue でネストを浅くする。
  • 肯定だけを書く: 本体はやりたい処理のみ。

1階層ずつ処理する

// ネスト配列の中身だけ欲しいなら flatMap を使う
const items = groups.flatMap(g => g.items);
JavaScript
  • 役割分離: 展開→抽出→変換の順に段階を分ける。

早期リターンの活用

関数の先頭でガード節

function toLabels(products) {
  if (!Array.isArray(products) || products.length === 0) return [];
  return products.filter(p => p?.stock > 0).map(p => `${p.name} (${p.price})`);
}
JavaScript
  • 不正は即返す: 正常系だけ下に伸ばすと読みやすい。
  • 分岐を減らす: ネストと条件の複雑さが下がる。

ループの途中終了

function findFirstValid(xs) {
  for (const x of xs) {
    if (x == null) continue;
    if (isValid(x)) return x; // 早期 return
  }
  return null;
}
JavaScript
  • 目的に合う終了: “見つかったら終了”は find/for…of+return。

副作用とデータ変換の分離

副作用は forEach、変換は map

// 変換(新配列が欲しい)
const names = users.map(u => u.name);

// 副作用(ログやDOM)
users.forEach(u => console.log(u.name));
JavaScript
  • 混ぜない: map の中で外部配列へ push しない。forEach は“実行”に徹する。
  • 非破壊の原則: 元配列を書き換えず、新配列へ結果を入れる。

インデックスと範囲の明確化

境界は開区間で書く

for (let i = 0; i < arr.length; i++) { /* … */ } // “未満”が鉄則
JavaScript
  • off-by-one 回避: <= は余分に回って undefined を読む。

インデックスが必要な時だけ使う

for (const [i, v] of arr.entries()) {
  // i と v の両方を明示できる
}
JavaScript
  • 値中心: 位置不要なら for…of で簡潔に。
  • 削除は逆順: splice するなら末尾から前へ。

非同期処理での可読性

順次なら for…of+await

async function fetchAll(ids) {
  const out = [];
  for (const id of ids) {
    const r = await fetch(`/api/${id}`);
    if (!r.ok) break; // 失敗で終了
    out.push(await r.json());
  }
  return out;
}
JavaScript
  • 待ちやすい: 1件ずつ確実に処理しやすい。

並列なら map+Promise.all

const results = await Promise.all(
  ids.map(id => fetch(`/api/${id}`).then(r => r.json()))
);
JavaScript
  • 短く明確: “並べて待つ”意図をはっきり表現。

命名と構造化の指針

名前で意図を語らせる

  • ラベル例: source: 入力配列、out: 出力配列、sum: 合計、hit: 最初の一致。
  • 関数分割: validate: 検証、format: 整形、collect: 収集。役割別に分けるとループが薄くなる。

高コストの準備は外で

const collator = new Intl.Collator("ja", { sensitivity: "base" });
const labels = rows.map(r => collator.compare(r.a, r.b) > 0 ? r.a : r.b);
JavaScript
  • 再利用: 正規表現や Collator の生成はループ外で一度だけ。

実践例の前後比較

before:目的が混ざって読みづらい

const labels = [];
for (let i = 0; i < products.length; i++) {
  const p = products[i];
  if (!p || p.stock <= 0) { /* 何もしない */ }
  else {
    const s = `${p.name} (${p.price})`;
    labels.push(s);
  }
}
JavaScript

after:役割分離で一目瞭然

const labels = products
  .filter(p => p?.stock > 0)            // 先に捨てる
  .map(p => `${p.name} (${p.price})`);  // 変換だけに集中
JavaScript

まとめ

ループの可読性は「目的に合う構文選択」「否定条件の早期排除」「非破壊の原則」「インデックス最小化」「非同期の設計分離」で決まります。抽出は filter、変換は map、集計は reduce、副作用は forEach、停止・待機は for…of。ガードでネストを浅く保ち、命名で意図を語らせ、重い準備は外へ。これを徹底すれば、初心者でも短く、意図が伝わり、壊れにくいループを書けます。

タイトルとURLをコピーしました