配列の変換メソッド — map・filter・reduce・forEach
この章の目次開く
- map — 各要素を変換して新しい配列を作る
- コールバックの引数
- map は要素数を変えられない
- 実務パターン: APIレスポンスの整形
- map を使うべきでない場面
- forEach — 各要素に対して処理を実行する
- 基本的な使い方
- 実務パターン: 各要素に副作用を実行する
- map との違い
- 途中で止められない
- filter — 条件に合う要素だけを残す
- 実務パターン: 検索フィルタリング
- filter + map のチェーン
- 該当なしの場合
- reduce — 配列を1つの値にまとめる
- reduce 自身の引数
- コールバックの引数
- 基本的な使い方
- 初期値は必ず指定する
- 配列の最大値を求める
- 実務パターン: グルーピング
- reduce を使いすぎない
- メソッドチェーンの実践パターン
- 途中で結果を確認したいとき
- ちゃんと使うためのポイント
- 参考リンク
map・filter・reduce は、配列メソッドの中でも特に使用頻度が高い3つです。
各要素に対して「何かするだけ」なら、そこに forEach が加わります。
それぞれの役割は、次のように覚えると整理しやすくなります。
| メソッド | 役割 | 戻り値 |
|---|---|---|
map | 各要素を変換する | 新しい配列 |
filter | 条件に合う要素だけ残す | 新しい配列 |
reduce | 配列全体を1つの値にまとめる | 任意の値 |
forEach | 各要素に処理を実行する | undefined |
map — 各要素を変換して新しい配列を作る

map は配列の各要素にコールバック関数を適用し、その戻り値で構成された新しい配列を返します。
元の配列は変更されません(非破壊)。
構文: array.map(callbackFn)
| 引数 | 渡せるもの | 説明 |
|---|---|---|
callbackFn | (element, index, array) => 値 | 各要素を変換する関数。その戻り値がそのまま要素になる |
戻り値: 変換結果からなる新しい配列。長さは必ず元の配列と同じ
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5](元の配列はそのまま)コールバックの引数
const result = ['a', 'b', 'c'].map((element, index, array) => {
// element: 現在の要素("a", "b", "c")
// index: 現在のインデックス(0, 1, 2)
// array: 元の配列自体
return `${index}: ${element}`;
});
// ["0: a", "1: b", "2: c"]map は要素数を変えられない
map の戻り値の長さは、呼び出した時点の元配列の長さと必ず一致します。コールバックが何を返しても、要素が増えたり減ったりすることはありません。
[1, 2, 3].map((n) => []); // [[], [], []] ← 長さは3のまま
[1, 2, 3].map((n) => undefined); // [undefined, undefined, undefined]map には「この要素はスキップする」という書き方がありません。return を書かなかった場合も要素が消えるわけではなく、undefined が入るだけです。
// ❌ 1より大きいものだけ残すつもりだが、除けていない
const result = [1, 2, 3].map((n) => {
if (n > 1) return n;
});
// [undefined, 2, 3] ← 長さは3のまま要素数を変えたいときは、目的に応じてメソッドを変えます。
| やりたいこと | 使うメソッド |
|---|---|
| 要素を減らしたい(絞り込み) | filter |
| 絞り込んだうえで変換もしたい | filter + map のチェーン |
| 1つの要素から複数の要素を作りたい | flatMap |
| 配列以外の形にまとめたい | reduce |
実務パターン: APIレスポンスの整形
const users = [
{ id: 1, first_name: '太郎', last_name: '田中' },
{ id: 2, first_name: '花子', last_name: '佐藤' },
];
const displayUsers = users.map((user) => ({
id: user.id,
fullName: `${user.last_name} ${user.first_name}`,
}));
// [{ id: 1, fullName: "田中 太郎" }, { id: 2, fullName: "佐藤 花子" }]map を使うべきでない場面
map は「変換した新しい配列が欲しいとき」に使います。
戻り値を使わずに副作用(ログ出力、DOM操作など)だけが目的なら forEach を使ってください。
// ❌ mapの戻り値を捨てている
users.map((user) => {
console.log(user.name);
});
// ⭕
users.forEach((user) => {
console.log(user.name);
});forEach — 各要素に対して処理を実行する

forEach は配列の各要素に対してコールバックを1回ずつ実行します。
map と違い、新しい配列は返しません。戻り値は常に undefined です。
ログ出力、通知の送信、既存オブジェクトへの書き込みなど、副作用そのものが目的のときに使います。
構文: array.forEach(callback)
| 引数 | 説明 |
|---|---|
callback | 各要素に対して実行する関数。(element, index, array) を受け取れる |
基本的な使い方
const fruits = ['りんご', 'みかん', 'ぶどう'];
fruits.forEach((fruit) => {
console.log(fruit);
});
// りんご
// みかん
// ぶどうインデックスが必要なときは第2引数を使います。
fruits.forEach((fruit, index) => {
console.log(`${index + 1}番目: ${fruit}`);
});
// 1番目: りんご
// 2番目: みかん
// 3番目: ぶどう実務パターン: 各要素に副作用を実行する
新しい配列を作るのではなく、「1件ずつ何かする」処理に向いています。
const users = [
{ name: '太郎', email: 'taro@example.com' },
{ name: '花子', email: 'hanako@example.com' },
];
users.forEach((user) => {
sendWelcomeMail(user.email);
});オブジェクトのプロパティを集計用の箱に足していく、といった書き方もできます。ただし集約が目的なら reduce の方が意図が明確です。
const counts = {};
['apple', 'banana', 'apple'].forEach((fruit) => {
counts[fruit] = (counts[fruit] ?? 0) + 1;
});
console.log(counts); // { apple: 2, banana: 1 }map との違い
どちらも「各要素を順に見る」点は同じですが、戻り値と目的が違います。
| 比較 | forEach | map |
|---|---|---|
| 目的 | 副作用を実行する | 新しい配列を作る |
| 戻り値 | undefined | 新しい配列 |
| メソッドチェーン | できない | できる |
const numbers = [1, 2, 3];
const mapped = numbers.map((n) => n * 2);
console.log(mapped); // [2, 4, 6]
const fromForEach = numbers.forEach((n) => n * 2);
console.log(fromForEach); // undefined
学習者じゃあ、変換したいときは map、ログを出したいときは forEach って覚えればいい?
先生その理解で十分。戻り値を使うなら map、使わないなら forEach。戻り値を捨てる map
は、読んだ人が「変換結果はどこ?」と迷う原因になるよ。
途中で止められない
forEach のコールバックで return しても、その要素の処理を抜けるだけで、残りの要素は実行されます。break は使えません。
[1, 2, 3, 4].forEach((n) => {
if (n === 3) return; // 3 だけスキップ。ループは続く
console.log(n);
});
// 1
// 2
// 4途中で止めたいときは for...of を使います。
for (const n of [1, 2, 3, 4]) {
if (n === 3) break;
console.log(n);
}
// 1
// 2const ids = [1, 2, 3];
// ❌ ほぼ同時に走り、完了を待てない
ids.forEach(async (id) => {
await fetch(`/api/users/${id}`);
});
// ⭕ 1件ずつ待つ
for (const id of ids) {
await fetch(`/api/users/${id}`);
}filter — 条件に合う要素だけを残す
filter はコールバックが true を返した要素だけで構成された新しい配列を返します。
元の配列は変更されません。
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4, 6]実務パターン: 検索フィルタリング
const products = [
{ name: 'ノートPC', price: 120000, inStock: true },
{ name: 'マウス', price: 3000, inStock: true },
{ name: 'キーボード', price: 15000, inStock: false },
{ name: 'モニター', price: 45000, inStock: true },
];
const result = products.filter((p) => p.inStock && p.price >= 10000);
// [{ name: "ノートPC", ... }, { name: "モニター", ... }]filter + map のチェーン
filter で絞り込んでから map で変換する、というチェーンは実務で非常によく書きます。
const inStockNames = products.filter((p) => p.inStock).map((p) => p.name);
// ["ノートPC", "マウス", "モニター"]該当なしの場合
条件に合う要素がなければ空配列 [] が返ります。null や undefined ではありません。
const result = [1, 2, 3].filter((n) => n > 10);
console.log(result); // []
console.log(result.length); // 0reduce — 配列を1つの値にまとめる

map や filter が新しい配列を返すのに対し、reduce が返すのは配列ではなく1つの値です。
手元に集計用の箱を1つ用意して、配列の要素を先頭から順に箱へ入れていく作業だと考えてください。要素を1つ処理するたびに箱の中身を更新し、最後に残った箱の中身がそのまま reduce の戻り値になります。箱に入れるものは数値でもオブジェクトでも配列でもよいため、合計・グルーピング・別の形への変換まで、集計全般をこれ1つで書けます。
構文: array.reduce(callbackFn, initialValue)
reduce 自身の引数
reduce が受け取るのは、要素ごとに呼ばれる関数と、箱の初期値の2つです。
| 引数 | 渡せるもの | 説明 |
|---|---|---|
callbackFn(第1引数) | (accumulator, currentValue, currentIndex, array) => 値 | 要素ごとに1回呼ばれる関数。その戻り値が次の accumulator になる |
initialValue(第2引数・省略可) | 任意の値 | 箱の初期値。省略した場合の挙動は後述(省略しないことを推奨) |
戻り値: 最後の呼び出しで callbackFn が返した値。要素が1つもなく initialValue だけを渡した場合は、initialValue がそのまま返ります。
コールバックの引数
callbackFn は4つの引数を受け取れます。実務で書くのはほとんど先頭2つですが、残り2つもこの順番で必ず渡ってきます。
| 引数 | 渡ってくる値 | 説明 |
|---|---|---|
第1引数 accumulator | 途中経過の箱 | 初回は initialValue、2回目以降は前回のコールバックの戻り値 |
第2引数 currentValue | 今見ている要素 | 初期値ありなら先頭要素から、初期値なしなら2番目の要素から始まる |
第3引数 currentIndex | currentValue の位置 | 初期値ありなら 0 から、初期値なしなら 1 から始まる |
第4引数 array | reduce を呼んだ配列自体 | 走査中の元配列。「最後の要素かどうか」の判定などに使う |
引数名は自由に決められます。実務では accumulator を acc、currentValue を cur や item と略すのが一般的です。
const joined = ['a', 'b', 'c'].reduce(
(accumulator, currentValue, currentIndex, array) => {
// accumulator: 前回の戻り値(初回は初期値の "")
// currentValue: 現在の要素("a", "b", "c")
// currentIndex: 現在のインデックス(0, 1, 2)
// array: 元の配列自体(["a", "b", "c"])
const isLast = currentIndex === array.length - 1;
return accumulator + currentValue + (isLast ? '' : ', ');
},
'',
);
// "a, b, c"基本的な使い方
もっとも単純な例が合計です。箱の初期値を 0 にして、要素を順に足していきます。
const numbers = [1, 2, 3];
const sum = numbers.reduce((accumulator, current) => {
return accumulator + current;
}, 0);
// 6このときコールバックは要素の数だけ、つまり3回呼ばれます。1回ごとの引数と戻り値は次のとおりです。
| 呼び出し | accumulator | currentValue | currentIndex | 戻り値 |
|---|---|---|---|---|
| 1回目 | 0(初期値) | 1 | 0 | 1 |
| 2回目 | 1(1回目の戻り値) | 2 | 1 | 3 |
| 3回目 | 3(2回目の戻り値) | 3 | 2 | 6 |
3回目の戻り値 6 が、そのまま reduce の戻り値になります。
reduce 全体の返り値です。
学習者reduce だけ急に難しい…!accumulator って何者…?
先生上の図の箱だよ。accumulator は途中経過を入れておく箱で、要素を1つ処理するたびに中身が更新される。
最後に残った箱が戻り値——合計なら数字、グルーピングならオブジェクトといった具合
ちなみに、accumulate は積み重ねる・蓄積するという意味です。
初期値は必ず指定する
初期値(第2引数)は省略できますが、省略すると挙動が次のように変わります。
accumulatorの初回の値が配列の先頭要素になるcurrentValueは2番目の要素から始まるcurrentIndexは0ではなく1から始まる- コールバックの呼び出し回数が、要素数より1回少なくなる
// 初期値あり: コールバックは3回呼ばれる(index は 0, 1, 2)
[1, 2, 3].reduce((acc, cur) => acc + cur, 0); // 6
// 初期値なし: acc の初回は 1、コールバックは2回だけ呼ばれる(index は 1, 2)
[1, 2, 3].reduce((acc, cur) => acc + cur); // 6合計のように「初期値と要素が同じ型」なら結果は変わりませんが、空配列に対して初期値なしで reduce を呼ぶと TypeError になります。初期値は常に指定するのが安全です。
// ❌ 空配列でエラー
[].reduce((acc, cur) => acc + cur);
// TypeError: Reduce of empty array with no initial value
// ⭕ 初期値を指定すれば空配列でも安全
[].reduce((acc, cur) => acc + cur, 0); // 0配列の最大値を求める
合計以外にも、要素同士を比較して1つの値に絞り込む処理にも reduce が使えます。
const scores = [72, 95, 58, 88, 100];
const max = scores.reduce((acc, cur) => Math.max(acc, cur), -Infinity);
// 100初期値に -Infinity を指定することで、どんな数値と比較しても最初の要素が必ず採用されます。
実務パターン: グルーピング
const orders = [
{ product: 'PC', category: 'electronics' },
{ product: 'シャツ', category: 'clothing' },
{ product: 'マウス', category: 'electronics' },
{ product: 'パンツ', category: 'clothing' },
];
const grouped = orders.reduce((acc, order) => {
const key = order.category;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(order);
return acc;
}, {});なお、このグルーピングは ES2024 で追加された Object.groupBy でより簡潔に書けるようになりました。
const grouped = Object.groupBy(orders, (order) => order.category);reduce を使いすぎない
reduce は汎用的すぎるがゆえに、何をしているか読みにくくなりがちです。
map + filter で書ける処理を無理に reduce で書く必要はありません。
// ❌ reduceで書いているが、やっていることはfilter + map
const result = numbers.reduce((acc, n) => {
if (n > 3) acc.push(n * 2);
return acc;
}, []);
// ⭕ 意図が明確
const result = numbers.filter((n) => n > 3).map((n) => n * 2);メソッドチェーンの実践パターン
配列メソッドを連鎖させるメソッドチェーンは、実務で最もよく書くパターンの一つです。
const transactions = [
{ id: 1, type: 'income', amount: 50000, date: '2024-06' },
{ id: 2, type: 'expense', amount: 3000, date: '2024-06' },
{ id: 3, type: 'expense', amount: 15000, date: '2024-07' },
{ id: 4, type: 'income', amount: 80000, date: '2024-07' },
{ id: 5, type: 'expense', amount: 5000, date: '2024-06' },
];
const juneExpenses = transactions
.filter((t) => t.type === 'expense')
.filter((t) => t.date === '2024-06')
.reduce((sum, t) => sum + t.amount, 0);
// 8000チェーンは上から下に「絞り込み → 変換 → 集約」の順で読めるのが理想です。
途中で結果を確認したいとき
チェーンの途中で console.log を挟みたい場合、一時変数に切り出すと確認しやすくなります。
const expenses = transactions.filter((t) => t.type === 'expense');
console.log('expenses:', expenses);
const total = expenses.reduce((sum, t) => sum + t.amount, 0);ちゃんと使うためのポイント
mapは変換した新しい配列が欲しいときに使うmapの戻り値は必ず元と同じ長さ。要素を減らすならfilter、増減させるならflatMapやreduce- コールバックで
returnを書き忘れても要素は消えず、undefinedが入る - 副作用だけが目的なら
forEach。戻り値を捨てるmapは書かない forEachは途中で止められない。breakや順次のawaitが必要ならfor...offilterは条件に合う要素だけを残したいときに使うreduceは合計、グルーピング、オブジェクトへの変換などの集約で使うreduceで何でも書けるが、読みやすさではfilter+mapの方がよいことも多い
次は、条件に合う要素を探したり判定したりする 配列の検索・判定メソッド を見ていきます。