JavaScriptで配列を操作するとき、map・filter・reduceは最も重要な3つのメソッドです。この3つを使いこなせるかどうかで、コードの読みやすさと保守性が大きく変わります。
forループで書いていた処理をmap・filter・reduceに置き換えるだけで、何をしているかが一目でわかるコードになります。実務では配列操作の9割以上をこの3つとメソッドチェーンで表現できます。
この記事では、基本構文から実務で使える実践パターンまで、豊富なコード例とともに解説します。
この記事で学べること
- map・filter・reduceそれぞれの基本構文と動作原理
- 実務で頻出する配列操作パターン
- メソッドチェーンによる組み合わせテクニック
- forEach・for…ofとの違いと使い分け
- よくあるミスと回避方法
map・filter・reduceとは
map・filter・reduceはいずれも配列のメソッドで、配列の各要素に対して処理を行い、新しい値を返すのが共通の特徴です。元の配列を変更しない(非破壊的)という点も重要です。
まずは3つのメソッドの役割を早見表で確認しましょう。
| メソッド |
役割 |
戻り値 |
イメージ |
| map() |
各要素を変換する |
新しい配列(同じ長さ) |
[1,2,3] → [2,4,6] |
| filter() |
条件に合う要素を抽出する |
新しい配列(同じか短い) |
[1,2,3,4] → [2,4] |
| reduce() |
全要素を1つの値に集約する |
任意の値(数値、文字列、オブジェクト等) |
[1,2,3] → 6 |
使い分けの基本:「各要素を別の値に変えたい」→ map、「条件で絞りたい」→ filter、「1つの値にまとめたい」→ reduce。迷ったらこの判断基準で選べばOKです。
map()の使い方
map()は配列の各要素にコールバック関数を適用し、その戻り値で構成された新しい配列を返します。元の配列と同じ長さの配列が必ず返る点が特徴です。
基本構文と動作
JavaScript
// 基本構文
const 新しい配列 = 配列.map((要素, インデックス, 元の配列) => {
return 変換後の値;
});
コールバック関数には3つの引数が渡されます。
- 第1引数:現在の要素(必須)
- 第2引数:インデックス番号(省略可)
- 第3引数:元の配列(省略可、ほぼ使わない)
配列の各要素を変換する
最もシンプルな例として、数値配列の各要素を2倍にしてみます。
JavaScript
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled);
console.log(numbers); // 元の配列は変わらない
実行結果
[2, 4, 6, 8, 10]
[1, 2, 3, 4, 5]
文字列の配列にも使えます。
JavaScript
const names = ["alice", "bob", "charlie"];
// 先頭を大文字にする
const capitalized = names.map(name => {
return name.charAt(0).toUpperCase() + name.slice(1);
});
console.log(capitalized);
実行結果
["Alice", "Bob", "Charlie"]
オブジェクト配列から特定プロパティを取り出す
実務では配列の中にオブジェクトが入っているケースが大半です。mapを使えば特定のプロパティだけを抽出できます。
JavaScript
const users = [
{ id: 1, name: "田中", age: 28 },
{ id: 2, name: "佐藤", age: 35 },
{ id: 3, name: "鈴木", age: 22 }
];
// 名前だけの配列を作る
const names = users.map(user => user.name);
console.log(names);
// 表示用の文字列に変換
const labels = users.map(user => `${user.name}(${user.age}歳)`);
console.log(labels);
実行結果
["田中", "佐藤", "鈴木"]
["田中(28歳)", "佐藤(35歳)", "鈴木(22歳)"]
オブジェクトの形を変換するパターンも実務で頻出します。
JavaScript
// APIレスポンスをUIコンポーネント用に変換
const products = [
{ product_id: 101, product_name: "ノートPC", price: 89800 },
{ product_id: 102, product_name: "マウス", price: 2980 },
{ product_id: 103, product_name: "キーボード", price: 7980 }
];
const options = products.map(p => ({
value: p.product_id,
label: `${p.product_name}(¥${p.price.toLocaleString()})`
}));
console.log(options);
実行結果
[
{ value: 101, label: "ノートPC(¥89,800)" },
{ value: 102, label: "マウス(¥2,980)" },
{ value: 103, label: "キーボード(¥7,980)" }
]
mapでオブジェクトリテラルを返すときは () => ({ ... }) のように括弧で囲む必要があります。括弧がないと {} がブロック文として解釈されてしまいます。
indexを活用する
コールバック関数の第2引数にはインデックス番号が渡されます。連番を振りたいときや、位置に応じた処理をしたいときに便利です。
JavaScript
const fruits = ["りんご", "バナナ", "みかん"];
// 1始まりの番号を付ける
const numbered = fruits.map((fruit, index) => {
return `${index + 1}. ${fruit}`;
});
console.log(numbered);
実行結果
["1. りんご", "2. バナナ", "3. みかん"]
Reactなどでリストをレンダリングするとき、indexをkeyに使うパターンもよく見かけます(ただし要素の並び替えがある場合はidを使うべきです)。
filter()の使い方
filter()は配列の各要素にコールバック関数を適用し、trueを返した要素だけで構成された新しい配列を返します。条件に一致する要素を抽出したいときに使います。
基本構文と動作
JavaScript
// 基本構文
const 新しい配列 = 配列.filter((要素, インデックス, 元の配列) => {
return 条件式; // trueなら残す、falseなら除外
});
引数の構成はmapと同じです。違いは戻り値がbooleanとして評価される点です。
条件に合う要素だけ抽出
JavaScript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 偶数だけ抽出
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens);
// 5より大きい数だけ抽出
const big = numbers.filter(num => num > 5);
console.log(big);
実行結果
[2, 4, 6, 8, 10]
[6, 7, 8, 9, 10]
文字列の配列でも同様に使えます。
JavaScript
const words = ["JavaScript", "CSS", "HTML", "TypeScript", "React"];
// 5文字以上の単語だけ抽出
const longWords = words.filter(word => word.length >= 5);
console.log(longWords);
// "Script"を含む単語を抽出
const scripts = words.filter(word => word.includes("Script"));
console.log(scripts);
実行結果
["JavaScript", "TypeScript", "React"]
["JavaScript", "TypeScript"]
falsyな値を除去する
配列からnull、undefined、空文字、0、falseなどのfalsyな値を一括除去するテクニックです。APIレスポンスの整形でよく使います。
JavaScript
const mixed = ["hello", 0, "", null, "world", undefined, 42, false];
// Booleanコンストラクタをそのまま渡すテクニック
const truthy = mixed.filter(Boolean);
console.log(truthy);
実行結果
["hello", "world", 42]
.filter(Boolean) は .filter(x => Boolean(x)) の省略形です。各要素をBooleanに変換し、trueになる要素だけ残します。0やfalseも除去される点に注意してください。
オブジェクト配列の絞り込み
実務で最も使用頻度が高いパターンです。
JavaScript
const employees = [
{ name: "田中", department: "開発", salary: 450000 },
{ name: "佐藤", department: "営業", salary: 380000 },
{ name: "鈴木", department: "開発", salary: 520000 },
{ name: "高橋", department: "営業", salary: 410000 },
{ name: "伊藤", department: "開発", salary: 480000 }
];
// 開発部のメンバーだけ抽出
const devTeam = employees.filter(emp => emp.department === "開発");
console.log(devTeam);
// 年収400万以上のメンバー
const highSalary = employees.filter(emp => emp.salary >= 400000);
console.log(highSalary.map(emp => emp.name));
実行結果
[
{ name: "田中", department: "開発", salary: 450000 },
{ name: "鈴木", department: "開発", salary: 520000 },
{ name: "伊藤", department: "開発", salary: 480000 }
]
["田中", "鈴木", "高橋", "伊藤"]
複数条件を組み合わせることもできます。
JavaScript
// 開発部かつ年収45万以上
const seniorDevs = employees.filter(emp => {
return emp.department === "開発" && emp.salary >= 450000;
});
console.log(seniorDevs.map(e => e.name));
reduce()の使い方
reduce()は配列の全要素を1つの値に集約(畳み込み)するメソッドです。map・filterと比べて概念が難しく感じますが、「配列の要素を左から順に処理して、結果を蓄積していく」イメージで理解できます。
基本構文と動作(初期値の重要性)
JavaScript
// 基本構文
const 結果 = 配列.reduce((累積値, 現在の要素, インデックス, 元の配列) => {
return 新しい累積値;
}, 初期値);
コールバック関数の引数は以下の通りです。
- 第1引数(累積値 / accumulator):前回のコールバックの戻り値(初回は初期値)
- 第2引数(現在の要素):処理中の要素
- 第3引数(インデックス):省略可
- 第4引数(元の配列):省略可
注意:reduceの第2引数(初期値)は必ず指定することを強く推奨します。省略すると配列の最初の要素が初期値になりますが、空配列でエラーが発生する原因になります。詳しくは「よくあるミス」のセクションで解説します。
reduceの動作を図解するために、合計を求める例を1ステップずつ見てみましょう。
JavaScript
const nums = [10, 20, 30];
const total = nums.reduce((acc, cur) => {
console.log(`acc=${acc}, cur=${cur}, 結果=${acc + cur}`);
return acc + cur;
}, 0);
console.log("合計:", total);
実行結果
acc=0, cur=10, 結果=10
acc=10, cur=20, 結果=30
acc=30, cur=30, 結果=60
合計: 60
| ステップ |
acc(累積値) |
cur(現在の要素) |
戻り値 |
| 1回目 |
0(初期値) |
10 |
10 |
| 2回目 |
10 |
20 |
30 |
| 3回目 |
30 |
30 |
60 |
合計・平均の計算
reduceの最も基本的な使い方です。
JavaScript
const scores = [85, 92, 78, 96, 64];
// 合計
const sum = scores.reduce((acc, score) => acc + score, 0);
console.log("合計:", sum);
// 平均
const average = scores.reduce((acc, score) => acc + score, 0) / scores.length;
console.log("平均:", average);
オブジェクト配列の特定プロパティを合計するパターンも頻出です。
JavaScript
const cart = [
{ name: "りんご", price: 200, quantity: 3 },
{ name: "バナナ", price: 150, quantity: 2 },
{ name: "みかん", price: 100, quantity: 5 }
];
const totalPrice = cart.reduce((acc, item) => {
return acc + item.price * item.quantity;
}, 0);
console.log(`合計金額: ¥${totalPrice.toLocaleString()}`);
配列をオブジェクトに変換
reduceの初期値をオブジェクト {} にすれば、配列をオブジェクトに変換できます。IDをキーにしたルックアップテーブルを作るパターンは実務で非常に役立ちます。
JavaScript
const users = [
{ id: 1, name: "田中" },
{ id: 2, name: "佐藤" },
{ id: 3, name: "鈴木" }
];
// IDをキーにしたオブジェクトに変換
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap);
console.log(userMap[2].name);
実行結果
{ 1: { id: 1, name: "田中" }, 2: { id: 2, name: "佐藤" }, 3: { id: 3, name: "鈴木" } }
佐藤
グルーピング(カテゴリ別に分類)するパターンも実務でよく使います。
JavaScript
const items = [
{ name: "りんご", category: "果物" },
{ name: "トマト", category: "野菜" },
{ name: "バナナ", category: "果物" },
{ name: "にんじん", category: "野菜" },
{ name: "みかん", category: "果物" }
];
const grouped = items.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = [];
}
acc[item.category].push(item.name);
return acc;
}, {});
console.log(grouped);
実行結果
{
果物: ["りんご", "バナナ", "みかん"],
野菜: ["トマト", "にんじん"]
}
配列のフラット化
ネストした配列を1次元配列にするパターンです。Array.flat()がない環境でも使えます。
JavaScript
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat);
// スプレッド構文でも書ける
const flat2 = nested.reduce((acc, arr) => [...acc, ...arr], []);
console.log(flat2);
実行結果
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
現在のモダンブラウザでは Array.flat() や Array.flatMap() が使えるので、単純なフラット化にはそちらを使う方がシンプルです。reduceは「フラット化しながら別の処理も同時に行いたい」場合に有効です。
最大値・最小値の取得
JavaScript
const prices = [1200, 800, 3500, 450, 2100];
const max = prices.reduce((a, b) => a > b ? a : b);
const min = prices.reduce((a, b) => a < b ? a : b);
console.log("最大:", max);
console.log("最小:", min);
補足:単純な最大値・最小値なら Math.max(...prices) や Math.min(...prices) の方が簡潔です。ただし、配列が非常に大きい場合(数万要素以上)はスプレッド構文がスタックオーバーフローを起こす可能性があるため、reduceの方が安全です。
メソッドチェーンで組み合わせる
map・filter・reduceはいずれも呼び出し元の配列を変更せず新しい値を返すため、メソッドチェーンでつなげて使えます。これが配列メソッドの真価を発揮する場面です。
filter → map の実践例
「条件で絞り込んでから変換する」パターンは実務で最も頻出する組み合わせです。
JavaScript
const products = [
{ name: "ノートPC", price: 89800, inStock: true },
{ name: "マウス", price: 2980, inStock: true },
{ name: "モニター", price: 35800, inStock: false },
{ name: "キーボード", price: 7980, inStock: true },
{ name: "ヘッドセット", price: 12800, inStock: false }
];
// 在庫ありの商品名だけ取得
const availableNames = products
.filter(p => p.inStock)
.map(p => p.name);
console.log(availableNames);
実行結果
["ノートPC", "マウス", "キーボード"]
税込価格を計算して表示する例です。
JavaScript
// 在庫ありの商品を税込価格付きで表示
const availableWithTax = products
.filter(p => p.inStock)
.map(p => ({
name: p.name,
priceWithTax: Math.floor(p.price * 1.1),
label: `${p.name}: ¥${Math.floor(p.price * 1.1).toLocaleString()}(税込)`
}));
availableWithTax.forEach(p => console.log(p.label));
実行結果
ノートPC: ¥98,780(税込)
マウス: ¥3,278(税込)
キーボード: ¥8,778(税込)
map → reduce の実践例
「各要素を変換してから集計する」パターンです。
JavaScript
const orders = [
{ product: "りんご", unitPrice: 200, quantity: 3 },
{ product: "バナナ", unitPrice: 150, quantity: 5 },
{ product: "みかん", unitPrice: 80, quantity: 10 }
];
// 各商品の小計を計算してから合計を出す
const grandTotal = orders
.map(order => order.unitPrice * order.quantity)
.reduce((sum, subtotal) => sum + subtotal, 0);
console.log(`注文合計: ¥${grandTotal.toLocaleString()}`);
実務例:APIレスポンスの加工
実際のWeb開発でよくある、APIレスポンスを画面表示用に加工するパターンです。
JavaScript
// APIから取得したユーザーデータ(想定)
const apiResponse = [
{ id: 1, first_name: "Taro", last_name: "Tanaka", role: "admin", active: true },
{ id: 2, first_name: "Hanako", last_name: "Sato", role: "user", active: true },
{ id: 3, first_name: "Jiro", last_name: "Suzuki", role: "user", active: false },
{ id: 4, first_name: "Yuki", last_name: "Takahashi", role: "admin", active: true }
];
// アクティブなユーザーの表示用データを作成
const activeUsers = apiResponse
.filter(user => user.active)
.map(user => ({
id: user.id,
fullName: `${user.last_name} ${user.first_name}`,
isAdmin: user.role === "admin"
}));
console.log(activeUsers);
// 管理者の人数を集計
const adminCount = apiResponse
.filter(user => user.active && user.role === "admin")
.length;
console.log(`アクティブな管理者: ${adminCount}人`);
実行結果
[
{ id: 1, fullName: "Tanaka Taro", isAdmin: true },
{ id: 2, fullName: "Sato Hanako", isAdmin: false },
{ id: 4, fullName: "Takahashi Yuki", isAdmin: true }
]
アクティブな管理者: 2人
実務のコツ:メソッドチェーンが長くなるときは、各メソッドの前で改行してインデントを揃えると読みやすくなります。1つのチェーンが4段以上になる場合は、途中で変数に分けることも検討しましょう。
forEach・for…ofとの違い
配列を繰り返し処理するメソッドとして、map・filter・reduceの他にforEachとfor…ofがあります。それぞれの特徴と使い分けを整理します。
比較表
| 特徴 |
map / filter / reduce |
forEach |
for…of |
| 戻り値 |
あり(新しい配列 or 値) |
undefined |
なし(文) |
| チェーン |
可能 |
不可 |
不可 |
| break / continue |
不可 |
不可 |
可能 |
| async/await |
非推奨 |
非推奨 |
対応 |
| 主な用途 |
データ変換・抽出・集計 |
副作用のある処理 |
汎用的なループ |
戻り値の有無
最大の違いは戻り値の有無です。map・filter・reduceは新しい値を返しますが、forEachは常にundefinedを返します。
JavaScript
const numbers = [1, 2, 3];
// map: 新しい配列を返す
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
// forEach: undefinedを返す(戻り値は使えない)
const result = numbers.forEach(n => n * 2);
console.log(result); // undefined
使い分けの指針
| やりたいこと |
使うべきメソッド |
| 各要素を変換して新しい配列を作りたい |
map() |
| 条件に合う要素だけ取り出したい |
filter() |
| 全要素を1つの値にまとめたい |
reduce() |
| 各要素でDOM操作やログ出力などの副作用を実行したい |
forEach() |
| 途中でループを抜けたい / await を使いたい |
for…of |
原則:「結果の値が欲しい」なら map / filter / reduce、「処理を実行したいだけ」なら forEach か for…of。迷ったらまず map / filter / reduce で書けないか考えてみましょう。
よくあるミスと注意点
mapで元の配列を変更してしまう
mapは新しい配列を返しますが、コールバック内でオブジェクトのプロパティを直接変更すると、元の配列のオブジェクトも変更されてしまいます。
JavaScript(悪い例)
const users = [
{ name: "田中", age: 28 },
{ name: "佐藤", age: 35 }
];
// NG: 元のオブジェクトを直接変更してしまう
const updated = users.map(user => {
user.age += 1; // 元のオブジェクトを変更している!
return user;
});
console.log(users[0].age); // 29 ← 元の配列も変わっている
JavaScript(正しい例)
const users = [
{ name: "田中", age: 28 },
{ name: "佐藤", age: 35 }
];
// OK: 新しいオブジェクトを返す
const updated = users.map(user => ({
...user,
age: user.age + 1
}));
console.log(users[0].age); // 28 ← 元の配列は変わらない
console.log(updated[0].age); // 29
注意:mapの戻り値は新しい配列ですが、中身のオブジェクトは参照がコピーされるだけです。オブジェクトのプロパティを変更する場合は、スプレッド構文 { ...user } で新しいオブジェクトを作りましょう。
reduceの初期値を省略する危険性
reduceの第2引数(初期値)を省略すると、配列の最初の要素が初期値として使われます。これ自体は正しい動作ですが、空配列に対して実行するとエラーになります。
JavaScript(エラーが発生する例)
// 初期値なしで空配列をreduce → エラー!
const empty = [];
try {
const result = empty.reduce((acc, cur) => acc + cur);
} catch (e) {
console.log(e.message);
}
実行結果
Reduce of empty array with no initial value
JavaScript(安全な書き方)
// 初期値を指定すれば空配列でもエラーにならない
const empty = [];
const result = empty.reduce((acc, cur) => acc + cur, 0);
console.log(result); // 0
filterで参照型の比較ミス
オブジェクトや配列は参照型のため、=== で比較すると「同じ参照かどうか」を判定します。同じ内容でも別のオブジェクトならfalseになります。
JavaScript(意図通りに動かない例)
const items = [
{ id: 1, name: "Apple" },
{ id: 2, name: "Banana" },
{ id: 3, name: "Cherry" }
];
const target = { id: 2, name: "Banana" };
// NG: オブジェクト同士の === は参照の比較
const found = items.filter(item => item === target);
console.log(found.length); // 0 ← 見つからない!
// OK: プロパティで比較する
const found2 = items.filter(item => item.id === target.id);
console.log(found2.length); // 1
ポイント:オブジェクトをfilterで絞り込むときは、必ずプリミティブ値のプロパティ(id、nameなどの文字列や数値)で比較しましょう。
まとめ
JavaScriptの配列メソッドmap・filter・reduceについて、基本から実務パターンまで解説しました。
| メソッド |
用途 |
覚えておくこと |
| map() |
各要素を変換 |
元と同じ長さの配列が返る。オブジェクトはスプレッドでコピー |
| filter() |
条件で抽出 |
trueを返した要素だけ残る。参照型の比較に注意 |
| reduce() |
1つの値に集約 |
初期値を必ず指定する。初期値の型で結果の型が決まる |
この3つのメソッドを使いこなすための実践ステップです。
- forループをmapに置き換える:「新しい配列を作っている」forループを見つけたら、mapで書き直してみる
- if文での振り分けをfilterに置き換える:条件に合う要素を集めている処理をfilterで書き直す
- 合計や集計処理をreduceに置き換える:変数に加算していく処理をreduceで書き直す
- メソッドチェーンで組み合わせる:filter → map → reduce のパイプラインを意識する
最初はforループの方が直感的に感じるかもしれませんが、map・filter・reduceに慣れると「このコードは何をしているか」がメソッド名から一目で分かるようになります。ぜひ日常的に使って慣れていきましょう。
重複を削除する
filterとindexOfを組み合わせることで、配列の重複要素を除去できます。
JavaScript
const tags = ["JavaScript", "CSS", "JavaScript", "HTML", "CSS", "React"];
// filterで重複を除去する方法
const unique = tags.filter((tag, index, self) => {
return self.indexOf(tag) === index;
});
console.log(unique);
実行結果
["JavaScript", "CSS", "HTML", "React"]
この方法は、各要素が最初に出現するインデックスと現在のインデックスが一致するかどうかで判定しています。2回目以降の出現では indexOf が最初の位置を返すため、false になって除外されます。
現在のJavaScriptでは [...new Set(tags)] の方がシンプルです。ただしオブジェクトの重複除去にはSetが使えないため、filterパターンも覚えておくと便利です。
要素の出現回数をカウントする
reduceを使えば、配列の各要素が何回出現するかをオブジェクトで集計できます。
JavaScript
const votes = ["A", "B", "A", "C", "B", "A", "B", "C", "A"];
const counts = votes.reduce((acc, vote) => {
acc[vote] = (acc[vote] || 0) + 1;
return acc;
}, {});
console.log(counts);
実行結果
{ A: 4, B: 3, C: 2 }
このパターンはアンケート集計、アクセスログの解析、タグの使用頻度カウントなど、実務のさまざまな場面で活躍します。
3つを組み合わせた実務パターン
ECサイトの注文データを処理する総合的な例を見てみましょう。filter → map → reduce のパイプラインが自然に表現できます。
JavaScript
const orders = [
{ id: 1, customer: "田中", items: [
{ name: "シャツ", price: 3980, qty: 2 },
{ name: "パンツ", price: 5980, qty: 1 }
], status: "completed" },
{ id: 2, customer: "佐藤", items: [
{ name: "ジャケット", price: 12800, qty: 1 }
], status: "completed" },
{ id: 3, customer: "鈴木", items: [
{ name: "靴", price: 8900, qty: 1 }
], status: "cancelled" },
{ id: 4, customer: "高橋", items: [
{ name: "帽子", price: 2480, qty: 3 },
{ name: "マフラー", price: 3200, qty: 1 }
], status: "completed" }
];
// 完了済み注文の売上合計を算出
const totalRevenue = orders
.filter(order => order.status === "completed")
.map(order => {
const orderTotal = order.items.reduce((sum, item) => {
return sum + item.price * item.qty;
}, 0);
return { customer: order.customer, total: orderTotal };
})
.reduce((sum, order) => sum + order.total, 0);
console.log(`完了済み注文の売上合計: ¥${totalRevenue.toLocaleString()}`);
実行結果
完了済み注文の売上合計: ¥36,420
処理の流れを整理します。
- filter:キャンセル済み注文を除外(完了済みのみ残す)
- map:各注文の小計を計算し、顧客名と金額のオブジェクトに変換
- reduce:全注文の合計金額を算出
このように、filter → map → reduce のパイプラインで「何をしているか」が上から順に読み取れます。forループで同じ処理を書くと、ループ内にif文と計算ロジックが混在し、意図を読み取るのに時間がかかります。
mapの戻り値を使わないアンチパターン
mapは「変換結果の配列を返す」ためのメソッドです。戻り値を使わず副作用だけ目的で使うのはアンチパターンです。
JavaScript(アンチパターン)
const items = ["A", "B", "C"];
// NG: mapの戻り値を使っていない
items.map(item => {
console.log(item);
});
// OK: 副作用だけならforEachを使う
items.forEach(item => {
console.log(item);
});
注意:mapは不要な配列を生成するため、メモリの無駄遣いにもなります。ログ出力やDOM操作など「戻り値が不要な処理」にはforEachを使いましょう。ESLintの array-callback-return ルールを有効にすると、この問題を自動で検出できます。
パフォーマンスの考慮
メソッドチェーンは読みやすい反面、各メソッドで中間配列が生成されます。通常の開発ではまったく問題になりませんが、数十万件のデータを処理する場合はパフォーマンスに注意が必要です。
JavaScript
const largeArray = Array.from({ length: 100000 }, (_, i) => i);
// メソッドチェーン: 中間配列が2つ生成される
const result1 = largeArray
.filter(n => n % 2 === 0) // 中間配列1(50000要素)
.map(n => n * 2) // 中間配列2(50000要素)
.reduce((s, n) => s + n, 0);
// reduceで1回のループにまとめる(高速)
const result2 = largeArray.reduce((sum, n) => {
if (n % 2 === 0) {
return sum + n * 2;
}
return sum;
}, 0);
console.log(result1 === result2); // true
実務での判断基準:配列が数千件以下なら可読性を優先してメソッドチェーンを使いましょう。数万件を超える場合や、処理が頻繁に実行される場合(スクロールイベントなど)は、reduceで1ループにまとめることを検討してください。