JavaScript 配列の実践:filter・map・reduce のチェーン活用例
filter・map・reduce は単体でも使いますが、メソッドチェーンで組み合わせると複雑な処理を簡潔に書けます。実際のコードでよく登場するパターンをまとめます。
カートの合計金額を計算する
在庫あり・数量1以上の商品だけ対象にして合計金額を出す例です。
const cart = [
{ name: 'apple', price: 100, qty: 2, inStock: true },
{ name: 'banana', price: 80, qty: 0, inStock: false }, // 数量0
{ name: 'cherry', price: 200, qty: 1, inStock: true },
{ name: 'grape', price: 300, qty: 1, inStock: false }, // 在庫なし
]
const total = cart
.filter((item) => item.inStock && item.qty > 0) // 有効な商品だけ絞り込む
.map((item) => item.price * item.qty) // 小計に変換
.reduce((sum, amount) => sum + amount, 0) // 合計
// → 400(apple: 200 + cherry: 200)
filter → map → reduce は「絞り込む → 変換する → 集計する」という流れで、最もよく登場するパターンです。
API レスポンスを整形する
API から返ってきたデータを「有効なもの」だけに絞り、必要なフィールドだけに整形してソートする例です。
const users = [
{ id: 1, name: 'Alice', role: 'admin', deletedAt: null },
{ id: 2, name: 'Bob', role: 'user', deletedAt: '2024-01-01' }, // 削除済み
{ id: 3, name: 'Carol', role: 'admin', deletedAt: null },
{ id: 4, name: 'Dave', role: 'user', deletedAt: null },
]
const activeAdmins = users
.filter((user) => user.role === 'admin' && !user.deletedAt) // 有効な管理者だけ
.map((user) => ({ id: user.id, name: user.name })) // 必要なフィールドだけ
.sort((a, b) => a.name.localeCompare(b.name)) // 名前順
// → [{ id: 1, name: 'Alice' }, { id: 3, name: 'Carol' }]
タグの出現ランキングを作る
複数の記事・商品に付いたタグを集計して、出現回数順のランキングを作る例です。
const articles = [
{ title: '記事A', tags: ['js', 'react', 'typescript'] },
{ title: '記事B', tags: ['js', 'node'] },
{ title: '記事C', tags: ['react', 'typescript'] },
{ title: '記事D', tags: ['js', 'react'] },
]
const tagRanking = articles
.flatMap((article) => article.tags) // タグを1つの配列に展開
.reduce((acc, tag) => {
acc[tag] = (acc[tag] || 0) + 1
return acc
}, {}) // { js: 3, react: 3, typescript: 2, node: 1 }
const top3 = Object.entries(tagRanking)
.sort(([, a], [, b]) => b - a) // 出現回数の降順
.slice(0, 3) // 上位3件
.map(([tag, count]) => ({ tag, count })) // 整形
// → [{ tag: 'js', count: 3 }, { tag: 'react', count: 3 }, { tag: 'typescript', count: 2 }]
ユーザー別の売上合計を出す
注文データをユーザーごとにまとめて、売上の多い順に並べる例です。
const orders = [
{ user: 'Alice', amount: 1000 },
{ user: 'Bob', amount: 500 },
{ user: 'Alice', amount: 2000 },
{ user: 'Carol', amount: 1500 },
{ user: 'Bob', amount: 800 },
]
const ranking = Object.entries(
orders.reduce((acc, { user, amount }) => {
acc[user] = (acc[user] || 0) + amount
return acc
}, {}) // { Alice: 3000, Bob: 1300, Carol: 1500 }
)
.map(([user, total]) => ({ user, total })) // 配列のオブジェクトに整形
.sort((a, b) => b.total - a.total) // 売上降順
// → [{ user: 'Alice', total: 3000 }, { user: 'Carol', total: 1500 }, ...]
複数条件の検索フィルタリング
検索クエリ・価格上限・カテゴリで絞り込む例です。条件ごとに filter を分けると読みやすくなります。
const products = [
{ name: 'Apple juice', price: 200, category: 'drink' },
{ name: 'Apple pie', price: 600, category: 'food' },
{ name: 'Green apple', price: 150, category: 'fruit' },
{ name: 'Apple watch', price: 50000, category: 'gadget' },
]
const query = 'apple'
const maxPrice = 500
const category = 'fruit'
// 条件ごとに filter を分けると意図が読みやすい
const results = products
.filter((p) => p.name.toLowerCase().includes(query))
.filter((p) => p.price <= maxPrice)
.filter((p) => !category || p.category === category)
.sort((a, b) => a.price - b.price)
// → [{ name: 'Green apple', price: 150, category: 'fruit' }]
チェーンを書くときのポイント
// ❌ 読みにくい:何をしているか追いづらい const result = data.filter(x => x.active).map(x => x.value * 2).reduce((a, b) => a + b, 0) // ✅ 読みやすい:処理ごとに改行してコメントを添える const result = data .filter((x) => x.active) // 有効なものだけ .map((x) => x.value * 2) // 2倍に変換 .reduce((sum, v) => sum + v, 0) // 合計
filter→map→reduceの順が基本(絞り込んでから変換・集計)mapしてからfilterすると不要な変換が走るため、filterを先に- 中間結果を確認したいときは一時変数に切り出す
まとめ
| パターン | チェーン |
|---|---|
| 有効なものを集計する | filter → map → reduce |
| データを整形してソートする | filter → map → sort |
| ネストした配列を集計する | flatMap → reduce / Object.entries |
| グループ別に集計する | reduce(グループ化)→ Object.entries → sort |
filter・map・reduce 各メソッドの詳細は以下を参照してください。
- 「JavaScript 配列の絞り込み:filter・find・some の使い方」
- 「JavaScript 配列の変換・集計:map・reduce・flatMap の使い方」