【JavaScript】配列データを一つに凝縮!reduceとreduceRightで集計・結合を行う方法

目次

概要

配列内のデータを集計して「合計値」を出したり、複数の文字列を結合して「一つの文章」を作ったりする場合、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 とすることで特定のプロパティだけを合計できます。

注意点

  1. 空配列での挙動初期値を指定せずに空の配列で reduce を実行すると、TypeError が発生します。データが空になる可能性がある場合は、必ず初期値を設定してください。
  2. 可読性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: 順序が重要で、かつ「後ろから」処理したいときに使う(右から左)。

単純な合計計算だけでなく、データの形式変換やグルーピングなど、工夫次第で幅広い用途に対応できる強力なメソッドです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次