目次
概要
配列内のデータを集計して「合計値」を出したり、複数の文字列を結合して「一つの文章」を作ったりする場合、for 文や forEach を使うと一時変数の定義が必要になりコードが冗長になりがちです。
reduce メソッドを使えば、配列の各要素を順番に処理しながら単一の値へと「畳み込む」処理をスマートに記述できます。本記事では、物流システムの在庫集計と配送ルート表示を例に、その活用法を解説します。
仕様(入出力)
reduce / reduceRight メソッド
コールバック関数を利用して、配列の要素を単一の値(数値、文字列、オブジェクトなど)にまとめます。
| メソッド | 処理の方向 | 初期値の扱い |
reduce | 左から右へ (インデックス 0 → 末尾) | 指定がない場合、配列の最初の要素が初期値になります。 |
reduceRight | 右から左へ (末尾 → インデックス 0) | 指定がない場合、配列の最後の要素が初期値になります。 |
- 構文:
array.reduce((accumulator, currentValue, index, array) => { ... }, initialValue)accumulator(累積値): 前回のコールバック関数の戻り値。currentValue(現在の値): 現在処理されている要素。initialValue(初期値): 最初のaccumulatorとなる値(指定を推奨)。
- 戻り値: 最終的な計算結果(単一の値)。
基本の使い方
1. 数値の合計を計算する (reduce)
for 文を使わずに総和を求めます。初期値を 0 に設定するのがポイントです。
const sales = [1500, 2300, 800];
// prev: 前回までの合計, current: 現在の要素
const total = sales.reduce((prev, current) => {
return prev + current;
}, 0);
console.log(total); // 4600
2. 配列を平坦化する (reduce + concat)
2次元配列を1次元配列に結合します(現在は flat() メソッドもありますが、ロジック理解に役立ちます)。
const clusters = [['Area-A', 'Area-B'], ['Area-C']];
const flatArea = clusters.reduce((prev, current) => {
return prev.concat(current);
}, []); // 初期値は空配列
console.log(flatArea); // ["Area-A", "Area-B", "Area-C"]
3. 文字列を逆順に結合する (reduceRight)
配列の後ろから順に処理を行います。
const route = ['Start', 'Point-A', 'Goal'];
// 右(Goal)から左(Start)へ結合
const reverseRoute = route.reduceRight((prev, current) => {
return `${prev} <- ${current}`;
});
console.log(reverseRoute); // "Goal <- Point-A <- Start"
コード全文(HTML / JavaScript)
物流倉庫の管理画面を想定したデモです。
各倉庫の「在庫数の合計(reduce)」と、商品が辿ってきた「配送ルートの追跡表示(reduceRight)」を実装します。
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Logistics Dashboard</title>
<style>
.dashboard {
font-family: 'Segoe UI', sans-serif;
max-width: 500px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background-color: #f4f6f9;
}
h3 { margin-top: 0; color: #2c3e50; }
.section {
background: white;
padding: 15px;
border-radius: 6px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.data-label { font-size: 0.9em; color: #7f8c8d; margin-bottom: 5px; }
.data-value { font-size: 1.2em; font-weight: bold; color: #2980b9; }
.route-step {
display: inline-block;
padding: 4px 8px;
background: #ecf0f1;
border-radius: 4px;
margin: 2px;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="dashboard">
<h3>物流管理ダッシュボード</h3>
<div class="section">
<div class="data-label">全倉庫 在庫総数</div>
<div id="total-inventory" class="data-value">計算中...</div>
</div>
<div class="section">
<div class="data-label">配送トレーサビリティ (現在地 ← 出荷元)</div>
<div id="trace-route" class="data-value" style="font-size: 1em;">計算中...</div>
</div>
</div>
<script src="logistics.js"></script>
</body>
</html>
JavaScript
/**
* 物流管理スクリプト
* reduceによる集計とreduceRightによる逆順結合
*/
// 1. 各倉庫の在庫データ(数値配列)
const warehouseStocks = [120, 85, 40, 250, 90];
// 2. 配送経由地データ(文字列配列)
// [出荷元, 中継点1, 中継点2, ..., 現在地] の順で記録されている想定
const shippingRoute = [
'千葉中央倉庫',
'東京湾岸ハブ',
'埼玉配送センター',
'上尾営業所'
];
// DOM要素
const totalEl = document.getElementById('total-inventory');
const traceEl = document.getElementById('trace-route');
/**
* 在庫の総和を計算 (reduce)
*/
const calcTotalStock = () => {
// 初期値 0 からスタートし、現在の在庫数(stock)を足していく
const total = warehouseStocks.reduce((accumulator, stock) => {
return accumulator + stock;
}, 0);
totalEl.textContent = `${total.toLocaleString()} 個`;
};
/**
* 配送ルートを逆順で表示 (reduceRight)
* 現在地から出荷元へと遡る文字列を生成する
*/
const renderTraceRoute = () => {
// 配列の末尾(現在地)から先頭(出荷元)に向かって処理
// 初期値を指定せず、配列の最後の要素を最初の accumulator とする
const routeString = shippingRoute.reduceRight((prev, current) => {
return `${prev} ← ${current}`;
});
traceEl.textContent = routeString;
};
// 実行
calcTotalStock();
renderTraceRoute();
カスタムポイント
- 初期値の重要性:数値計算をする際、配列が空の場合のエラーを防ぐために、第2引数の初期値(
0など)は必ず指定する癖をつけましょう。const sum = [].reduce((a, b) => a + b, 0); // 0が返る - オブジェクト配列の集計:
[{ price: 100 }, { price: 200 }]のようなデータの場合、初期値を0にし、acc + cur.priceとすることで特定のプロパティだけを合計できます。
注意点
- 空配列での挙動初期値を指定せずに空の配列で
reduceを実行すると、TypeErrorが発生します。データが空になる可能性がある場合は、必ず初期値を設定してください。 - 可読性
reduceは非常に強力ですが、複雑なロジック(条件分岐や多重ループ)を詰め込むとコードが読みづらくなります。「単なる合計」や「単純な変換」以外でロジックが複雑になる場合は、素直にfor...ofループを使った方が可読性が高い場合があります。
応用
配列からオブジェクトを生成する(グルーピング)
配列データを、特定のキーで分類したオブジェクトに変換する高度な使い方です。
const items = ['Pen', 'Note', 'Pen', 'Box'];
// アイテムごとの出現回数をカウント
const count = items.reduce((acc, item) => {
// キーが存在しなければ初期化
if (!acc[item]) acc[item] = 0;
// カウントアップ
acc[item]++;
return acc;
}, {}); // 初期値は空のオブジェクト
console.log(count); // { Pen: 2, Note: 1, Box: 1 }
まとめ
reduce は配列操作の「万能ナイフ」です。
reduce: 配列を「合計値」や「新しい構造」にまとめるときに使う(左から右)。reduceRight: 順序が重要で、かつ「後ろから」処理したいときに使う(右から左)。
単純な合計計算だけでなく、データの形式変換やグルーピングなど、工夫次第で幅広い用途に対応できる強力なメソッドです。
