この記事の目次
map・filter・reduceの使い方とは
map・filter・reduce は、配列を「変換する」「絞り込む」「1つの値にまとめる」ための代表的なメソッドです。for 文で書いていた処理を、目的が見える形で短く書けます。3つとも元の配列を変更しません(新しい配列または値を返します)。
| メソッド | 目的 | 戻り値 |
|---|---|---|
| map | 各要素を変換 | 同じ長さの新しい配列 |
| filter | 条件に合う要素だけ残す | 同じか短い新しい配列 |
| reduce | 1つの値にまとめる | 任意の値(数値・オブジェクトなど) |
基本(構文・仕組み)
array.map((要素, インデックス, 元の配列) => 新しい要素)
array.filter((要素, インデックス, 元の配列) => 真偽値)
array.reduce((累積値, 要素, インデックス, 元の配列) => 次の累積値, 初期値)
filter のコールバックは真偽値(truthy / falsy)を返します。reduce の最後の引数「初期値」は省略できますが、空配列でエラーになるため基本は指定します。
サンプルコード(実行結果つき)
次の商品データで試します。
const items = [
{ n: 'pen', p: 120, q: 3 },
{ n: 'note', p: 300, q: 1 },
{ n: 'bag', p: 2500, q: 0 },
{ n: 'ink', p: 800, q: 2 }
];
map:名前の一覧を作る
console.log(items.map(i => i.n));
// [ 'pen', 'note', 'bag', 'ink' ]
filter→map:在庫がある商品だけ
console.log(items.filter(i => i.q > 0).map(i => i.n));
// [ 'pen', 'note', 'ink' ]
reduce:合計金額
console.log(items.reduce((s, i) => s + i.p * i.q, 0));
// 2260 (120*3 + 300*1 + 2500*0 + 800*2)
reduce:グループ分け
console.log(items.reduce((acc, i) => {
(acc[i.q > 0 ? 'stock' : 'out'] ||= []).push(i.n);
return acc;
}, {}));
// { stock: [ 'pen', 'note', 'ink' ], out: [ 'bag' ] }
空配列と初期値
[].reduce((a, b) => a + b);
// TypeError: Reduce of empty array with no initial value
console.log([].reduce((a, b) => a + b, 0)); // 0
mapでの落とし穴
console.log(['1', '2', '3'].map(parseInt)); // [ 1, NaN, NaN ]
console.log(['1', '2', '3'].map(Number)); // [ 1, 2, 3 ]
const nums = [1, 2, 3, 4, 5];
console.log(nums.map(n => { n * 2 }));
// [ undefined, undefined, undefined, undefined, undefined ]
parseInt は第2引数を基数(進数)として受け取るため、map が渡すインデックスと衝突します。波括弧付きのアロー関数で return を忘れると undefined の配列になります。
動作確認(検証環境と結果)
- 環境: Node.js v24.19.0(Windows 11)
- 上記コードを実行し、コメントの結果と一致することを確認(
||=を含めて動作) - 連結例
nums.filter(n => n % 2).map(n => n * 10).reduce((a, b) => a + b, 0)は90(1+3+5 を10倍して合計) map実行後に元の配列[1,2,3]が変化しないことも確認- 検証ログ:
C:Tempks2techtestsjs-map-filter-reduce.md
map・filter・reduceの使い方の注意点
mapは変換用です。戻り値を使わず副作用だけが目的ならforEachやfor...ofを使います。- 連結しすぎると、要素数が多いときに中間配列が増えます。非常に大きな配列では1回のループにまとめることも検討してください。
reduceは万能ですが、読みにくくなりがちです。単純な合計やグループ化以外では、filterやmapで分けた方が分かりやすい場合があります。- 元のオブジェクトを
mapの中で直接書き換えると、元の配列の要素も変わります(要素は参照のため)。
map・filter・reduceの使い方でよくあるミス
reduceの初期値を忘れ、空配列で TypeError になる。reduceのコールバックでreturn accを忘れ、次の反復でundefinedになる。map(parseInt)をそのまま書く。filterのコールバックで=と===を取り違える。- 結果を変数に受け取らず、元の配列が変わると思い込む。
ブラウザ対応・バージョン
3つとも ES5 以降の標準で、現行ブラウザは問題なく利用できます。Internet Explorer 9 以降でも対応していました。
map・filter・reduceの使い方のチェックリスト
- [ ] 目的に合うメソッド(変換=map、絞り込み=filter、集計=reduce)を選んだ
- [ ]
reduceに初期値を渡した - [ ] 波括弧付きアロー関数では
returnを書いた - [ ] 元の配列を変更したくない箇所で、新しい配列を受け取っている
- [ ]
map(parseInt)のような引数の衝突がない
map・filter・reduceの使い方のFAQ(よくある質問)
Q. forEach と map の違いは?
A. forEach は戻り値がなく副作用向け、map は新しい配列を返します。
Q. reduce の初期値は必ず必要ですか?
A. 省略すると最初の要素が初期値になります。ただし空配列でエラーになり、要素の型が変わる集計では間違いやすいので、指定を勧めます。
Q. オブジェクトにも使えますか?
A. 配列のメソッドなので、オブジェクトは Object.keys() や Object.entries() で配列に変換してから使います。
筆者の見解(map・filter・reduceの使い方)
私見では、map・filter・reduce は「全部 reduce で書く」より、目的ごとに分けて連結する方がレビューしやすいと考えます。reduce は柔軟な分、読む人に負担をかけます。集計やグループ化のようにパターンが決まっている場面に絞って使うのが、バランスが良いと思います。配列の基本は 膨大なデータの管理に便利!配列とオブジェクトを学ぼう!【JavaScript入門】 も参考にしてください。
map・filter・reduceの使い方の関連項目
- 膨大なデータの管理に便利!配列とオブジェクトを学ぼう!【JavaScript入門】
- アロー関数
- スプレッド構文
- 分割代入
- 【Javascript】オブジェクト(連想配列)内の文字列をソートする方法【sortメソッド/比較関数】
