JavaScriptで配列を集計するとき、reduceは便利だと分かっていても、連想配列のような形にまとめる処理になると急に難しく感じる方は多いです。特に、オブジェクトへ変換する書き方、初期値の考え方、foreachやmapとの使い分け、重複キーの扱いはつまずきやすいポイントです。
この記事では、reduceで連想配列風のデータを作る基本から実務的な活用法までを、コード例を交えながら分かりやすく整理します。読了後には、配列から目的のオブジェクトを安全かつ読みやすく生成できるようになります。
reduceとJavaScriptで連想配列を扱う基本
JavaScriptにはPHPなどで使われる意味での連想配列そのものは存在せず、一般的にはキーと値を持つオブジェクトやMapを連想配列のように扱います。そこで重要になるのがreduceです。reduceは配列を先頭から順に処理し、1つの結果へ畳み込むメソッドです。
配列の各要素を見ながら、キーを作って値を格納していけば、配列からオブジェクトへ変換できます。この考え方を理解すると、グルーピング、集計、インデックス化、重複データの整理など、多くの実務処理を短く表現できます。単に書けるだけでなく、初期値と戻り値の関係を把握することが正確な実装の第一歩です。
reduceの基本構文と戻り値の考え方
reduceの基本構文は、配列に対してコールバック関数を渡し、そこへ累積値と現在の要素を受け取る形です。累積値は前回までの処理結果で、最後にはreduce全体の戻り値になります。
連想配列風の結果を作る場合は、初期値に空オブジェクトを渡すのが定番です。各ループでそのオブジェクトにキーを追加し、最後に返します。たとえば商品配列からidをキーにした辞書を作るなら、累積値にacc、現在要素にitemを取り、acc[item.id] = itemという形で追加します。重要なのは、毎回必ず累積値を返すことです。これを忘れると次の周回でundefinedになり、意図しないエラーにつながります。
const users = [
{ id: 1, name: '田中' },
{ id: 2, name: '佐藤' }
];
const result = users.reduce((acc, user) => {
acc[user.id] = user.name;
return acc;
}, {});
// 結果
// { 1: '田中', 2: '佐藤' }
JavaScriptで連想配列と言うと何を指すのか
JavaScriptで連想配列という表現が使われるとき、多くはオブジェクトを指しています。ただし厳密には、配列は数値インデックスで管理される特殊なオブジェクトであり、文字列キー中心の用途には通常のオブジェクトやMapのほうが適しています。
オブジェクトは手軽で記述量が少なく、JSONとも相性が良い一方で、キーは文字列かSymbolとして扱われます。Mapは任意の型をキーにでき、要素順の扱いも明確です。検索用途ならオブジェクトで十分な場面が多いですが、キーの型や大量データの運用まで考えるならMapも選択肢に入ります。まずはreduceでオブジェクトを作る基本を身につけることが、理解の土台になります。
reduceでオブジェクトへ変換する代表パターン
reduceが便利なのは、単純な変換だけでなく、要件に応じて結果の形を自由に作れる点です。たとえば、idをキーにして1件ずつ参照しやすくしたい、カテゴリごとに商品をまとめたい、件数を数えたいといった場面で力を発揮します。
実務では、あとから検索しやすい構造へ加工しておくと、その後の描画やAPI連携が楽になります。ここでは、とくによく使う3つの型として、キー指定の変換、グルーピング、集計の作り方を整理します。reduceの用途が広く見えても、基本は何を累積したいかを先に決めることです。
配列をキー付きオブジェクトへ変換する
最も基本的な使い方は、配列の各要素から一意なキーを取り出し、そのキーでオブジェクトに格納する方法です。これにより、配列を毎回findで探す必要がなくなり、参照コードがすっきりします。
たとえば会員一覧から会員番号をキーにしたオブジェクトを作れば、ある番号の会員情報を素早く取得できます。特にフォーム選択値、APIレスポンス、設定一覧など、検索頻度が高いデータでは有効です。なお、キーが重複する場合は後勝ちになるため、仕様として上書きで良いのか、エラーにすべきか、配列で保持すべきかを先に決めておくと安全です。
グルーピング用の連想配列風データを作る
reduceは1対1の変換だけでなく、1対多のグルーピングにも向いています。たとえば注文データを都道府県別、記事一覧をカテゴリ別、社員情報を部署別にまとめる処理です。
この場合は、キーに対応する配列がまだなければ作成し、あればpushして追加します。結果として、1つのキーに複数要素が入る連想配列風のオブジェクトが得られます。集計前の中間構造としても非常に有用です。実務で多いのは、一覧画面をセクション表示したいときや、カテゴリごとの件数計算をしたいときです。reduceを使えば1回の走査でまとめられるため、可読性と処理効率の両面でメリットがあります。
const items = [
{ category: 'fruit', name: 'りんご' },
{ category: 'fruit', name: 'みかん' },
{ category: 'book', name: '辞典' }
];
const grouped = items.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = [];
}
acc[item.category].push(item);
return acc;
}, {});
件数や合計値をまとめて集計する
reduceは名前の通り集約に強く、連想配列風のオブジェクトへ数値を蓄積する使い方も定番です。カテゴリ別件数、担当者別売上、ステータス別の出現回数などは非常に相性が良いです。
書き方としては、キーがまだ存在しなければ0で初期化し、必要な数だけ加算します。単純なカウントなら1ずつ増やし、売上集計なら金額を足します。結果として、後続処理にそのまま使える集計表が得られます。集計はfor文でも書けますが、reduceなら入力と出力の関係が明確になり、1つの式としてまとまりやすい点が利点です。
reduceを使う際の注意点と他メソッドとの違い
reduceは便利ですが、何でもreduceにすれば良いわけではありません。単純な繰り返し処理ならforEachのほうが読みやすい場合もありますし、要素数そのものを変換したいならmapのほうが直感的です。さらに、オブジェクトを毎回スプレッド構文で複製する書き方は見た目がきれいでも、データ量によっては無駄なコストになりやすいです。
実務で重要なのは、短く書くことではなく、意図が読み取りやすく、保守しやすいことです。ここでは、初期値の落とし穴、mapやforEachとの使い分け、パフォーマンス面の注意を押さえます。
初期値を省略したときの落とし穴
reduceは初期値を省略することもできますが、連想配列風のオブジェクトを作る用途では基本的に省略しないほうが安全です。省略すると最初の要素が累積値として使われるため、配列要素がオブジェクトだった場合に意図しない構造から処理が始まります。
また、空配列に対して初期値なしでreduceを呼ぶとエラーになります。入力データが必ず1件以上あるとは限らない実務では、この仕様が不具合の原因になりがちです。空オブジェクトや空配列、0など、最終的に欲しい結果の型に合わせて初期値を明示することが大切です。これはバグ防止だけでなく、コードを読む人に戻り値の型を伝える意味でも有効です。
mapやforEachとの使い分け
mapは各要素を別の要素へ変換し、同じ長さの新しい配列を返すメソッドです。forEachは副作用を伴う繰り返しに向きます。一方、reduceは複数要素から1つの結果を作るときに使います。
この違いを整理すると、配列をそのまま別配列へ変えるならmap、DOM操作やログ出力ならforEach、オブジェクト化や集計ならreduceという選び方がしやすくなります。無理にreduceへ統一すると、かえって理解しにくいコードになることがあります。用途に合ったメソッドを選ぶことが、保守しやすいJavaScriptを書く近道です。
| メソッド | 主な目的 | 向いている例 |
|---|---|---|
| map | 各要素の変換 | 名前一覧を表示用文字列に変換 |
| forEach | 繰り返し処理 | 配列を順番に出力、状態更新 |
| reduce | 1つの結果へ集約 | 連想配列風オブジェクト作成、集計 |
スプレッド構文の多用とパフォーマンス
reduceの例として、return { …acc, [key]: value } のような書き方をよく見かけます。これはイミュータブルで見通しが良い反面、ループのたびにオブジェクトを複製するため、要素数が増えると非効率になりやすいです。
大量データや頻繁に呼ばれる処理では、acc[key] = value として同じオブジェクトを更新し、最後に返すほうが現実的です。もちろん、状態管理や関数型の流儀を重視する場面ではスプレッド構文にも意義があります。大切なのは、見た目の美しさだけで決めず、処理量と保守性のバランスを見ることです。
実務で役立つreduce活用例と書き方のコツ
実務では、単に動くコードよりも、将来読んだときに意図が分かるコードが求められます。reduceは柔軟性が高いぶん、短く詰め込みすぎると難読化しやすいメソッドでもあります。そこで、変数名、条件分岐、キーの重複対応、Mapの活用などを整理しておくと、チーム開発でも扱いやすくなります。
特に、データ変換の責務を明確にし、完成形を先にイメージしてからreduceを書くと、不要なネストや分岐を減らせます。ここでは、実務で差がつきやすい書き方のコツを紹介します。
読みやすいreduceを書くコツ
reduceを読みやすくするには、まず累積値の名前を用途に合わせて付けることが効果的です。accでも構いませんが、grouped、indexed、countsのように結果を表す名前にすると意図が伝わりやすくなります。
また、コールバック内で複雑な三項演算子を重ねるより、if文で素直に書いたほうが保守しやすい場面は多いです。さらに、キー生成の条件が複雑なら、その部分だけ別関数に切り出すのも有効です。reduceは1行で書けることより、後から見て安全に変更できることを優先すると失敗しにくいです。
重複キーをどう扱うかを先に決める
配列からオブジェクトへ変換する場合、同じキーが複数回現れる可能性があります。このとき、最後の値で上書きするのか、最初の値を保持するのか、配列にまとめるのかで実装が変わります。
たとえばマスターデータのインデックス化なら上書きでも問題ないことが多いですが、履歴データや分類処理では複数件を保持したいことがあります。仕様を決めずに書くと、あとから不具合に見える挙動が起きやすくなります。reduceを書く前に、キー重複時の期待動作を決めておくと、実装とテストの両方が安定します。
ObjectよりMapが向く場面もある
連想配列風のデータを扱うとき、常にオブジェクトが最適とは限りません。キーに文字列以外を使いたい、登録順を明確に保ちたい、存在確認やサイズ取得を明快にしたいならMapが便利です。
reduceの結果としてMapを返すことも可能で、初期値にnew Mapを使い、setで値を入れていきます。最終的にJSON化するならオブジェクトのほうが扱いやすいですが、アプリ内部のデータ構造としてはMapが適するケースもあります。用途に応じて選べるようになると、JavaScriptのデータ処理設計が一段深まります。
まとめ
reduceでJavaScriptの連想配列風データを扱うポイントは、配列を何に集約したいのかを最初に決めることです。オブジェクトへ変換するなら空オブジェクトを初期値にし、各要素からキーを作って値を格納します。グルーピングや集計にも応用でき、実務で頻出のデータ整形を1回の走査で表現できます。
一方で、初期値の省略、重複キーの未定義、スプレッド構文の多用には注意が必要です。mapやforEachとの違いも理解し、用途に合ったメソッドを選ぶことが重要です。まずは単純なオブジェクト変換から練習し、次にグルーピングや件数集計へ広げると、reduceの使いどころが自然に身につきます。
コメント