JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptで連続する範囲のオブジェクトをグループ化する方法

この記事では、userIdfromto というプロパティを持つオブジェクトの配列から、範囲が連続している(重なっている)要素を1つにまとめる方法を解説します。

元となるデータ

まず、次のようなオブジェクトの配列があるとします。

const arr = [
    {userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 6},
    {userId: "3t5bsFB4PJmA3oTnm", from: 7, to: 15},
    {userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 181},
    {userId: "3t5bsFB4PJmA3oTnm", from: 182, to: 190}
];

ここで求められているのは、あるオブジェクトの to の値と次のオブジェクトの from の値が連続している場合(例:to が6で、次の from が7)、それらを1つのオブジェクトに統合するJavaScript関数を作成することです。

期待される出力は以下のようになります。最初の2つのオブジェクトは範囲が連続しているため「1〜15」に、後ろの2つも同様に「172〜190」にまとめられています。

const output = [
    {userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 15},
    {userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 190}
];

実装コード

この問題は、配列の reduce() メソッドを使うことで簡潔に解決できます。reduce() を使えば、前の要素との比較結果を累積しながら処理を進められるため、連続判定との相性が非常に良いのがポイントです。

const arr = [
    {userId: "3t5bsFB4PJmA3oTnm", from: 1, to: 6},
    {userId: "3t5bsFB4PJmA3oTnm", from: 7, to: 15},
    {userId: "3t5bsFB4PJmA3oTnm", from: 172, to: 181},
    {userId: "3t5bsFB4PJmA3oTnm", from: 182, to: 190}
];

const groupByDuration = (arr = []) => {
    const result = arr.reduce((acc, val) => {
        // 累積配列の最後の要素を取得(空なら空オブジェクト)
        let last = acc[acc.length - 1] || {};

        // userIdが同じで、範囲が連続している場合は統合
        if (last.userId === val.userId && last.to + 1 === val.from) {
            last.to = val.to;
        } else {
            // 連続していない場合は新しい要素として追加
            acc.push({ userId: val.userId, from: val.from, to: val.to });
        }
        return acc;
    }, []);

    return result;
};

console.log(groupByDuration(arr));

コードの仕組み

処理の流れを簡単に整理すると、以下のようになります。

  • 初期値: 空の配列 [] をアキュムレータとして設定します。
  • 最後の要素の取得: acc[acc.length - 1] || {} により、現在までに作成された最後のグループを取得します。配列が空の場合は空オブジェクトが返ります。
  • 連続判定: 直前の要素と userId が一致し、かつ直前の to + 1 が現在の from と等しい場合のみ、範囲を統合します。
  • 統合処理: 条件を満たす場合は、既存のオブジェクトの to を更新するだけで済むため、メモリ効率も良好です。
  • 新規追加: 条件を満たさない場合は、そのまま新しいオブジェクトとして配列に追加します。

出力結果

コンソールに出力される結果は次のとおりです。

[
    { userId: '3t5bsFB4PJmA3oTnm', from: 1, to: 15 },
    { userId: '3t5bsFB4PJmA3oTnm', from: 172, to: 190 }
]

応用ポイント

この手法は、予約システムやカレンダー機能など、時間帯・番号区間を扱う場面で特に役立ちます。もし「完全に隣接していなくても、範囲が重複していれば統合したい」というケースであれば、判定条件を last.to + 1 === val.from ではなく val.from <= last.to + 1 のように変更することで対応できます。


  1. 【初心者向け】JavaScriptのreverse()メソッドで配列を逆順にする方法

    JavaScriptのreverse()メソッドは、配列の要素を元の順序と逆順に入れ替えるための便利な関数です。このメソッドを呼び出すと、配列の最初の要素が最後に、最後の要素が最初に移動し、配列全体が反転されます。reverse()メソッドの基本reverse()は配列そのものを変更する「破壊的メソッド」である点に注意してください。つまり、元の配列の順序が直接書き換えられます。元の配列を保持したい場合は、あらかじめslice()やスプレッド構文([...arr])などでコピーを作成してからreverse()を使用するのがおすすめです。サンプルコード以下は、ボタンをクリックすると配列の要素が逆順

  2. JavaScriptにおける配列の分割代入(Destructuring)の基本と使い方

    分割代入(Destructuring)とは、配列から値を取り出して個別の変数に展開するための構文です。ES2015(ES6)で導入されたこの機能を使うと、配列の各要素を簡潔かつ読みやすく変数に割り当てることができます。 配列の分割代入のサンプルコード 以下は、JavaScriptで配列の分割代入を行うコード例です。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport&quo