JavaScriptのObject.groupBy():配列のデータを担当別にグループ化する
Object.groupBy()で配列を担当別に分類する方法を解説。引数と戻り値、ブラウザーとNode.jsの対応・実行手順、参照共有や非同期処理の注意点を自己完結したコードで学びます。
問い合わせ一覧を担当チームごとに表示したいとき、グループ用の配列を作り、該当するデータを追加する処理が必要になります。Object.groupBy()を使うと、各データの「所属先を決める関数」を渡すだけで、グループ別の配列を持つオブジェクトを作れます。
この記事では、問い合わせを担当別に分類する例を通して、呼び出し方と実務で間違えやすい点を学びます。外部ライブラリーや通信は不要です。
何を解決するAPIなのか
Object.groupBy()は、配列などの反復可能な値を受け取り、コールバックが返すキーごとに要素をまとめる静的メソッドです。配列自身ではなく、Objectから呼び出します。
例えば、担当が開発の問い合わせをdev、サポートの問い合わせをsupportというキーにまとめれば、担当別の一覧を取り出せます。元の問い合わせを削除したり、担当を変更したりする必要はありません。
分類結果には各グループの要素が残ります。件数や合計だけが必要なら、その後に集計する設計になります。分類と集計を分けることで、一覧表示と件数表示に同じ結果を使えます。
動作環境と対応バージョン
2026年9月27日時点で確認した主要ブラウザーの対応開始バージョンは、Chrome・Edge 117、Firefox 119、Safari 17.4です。対応状況はweb.devの2024年3月の更新記事で確認できます。古いブラウザーでは、そのまま呼び出せるとは限りません。
Node.jsでも利用できます。配列グループ化はNode.js 21のリリース告知で導入されています。ただし、21系はすでにサポート終了です。本記事を試す環境には、公式リリース一覧でLTSとして掲載されている24系を使用してください。22系でもこのAPIを利用できます。
以下のサンプルはブラウザーとNode.jsの両方に対応します。DOMやファイル操作は使わず、結果をconsole.log()で出力します。実行時にはtypeof Object.groupBy === "function"でAPIの存在も確認します。
引数と戻り値を理解する
呼び出しはObject.groupBy(items, callbackFn)という形です。
itemsは分類する反復可能な値です。この記事では配列を渡します。callbackFnは各要素に対して実行される関数です。引数として現在の要素と、0から始まる処理位置を受け取ります。- コールバックの戻り値がグループのキーになります。文字列または
Symbolを使用でき、それ以外の値は文字列に変換されます。 - API全体の戻り値は、各キーに要素の配列を持つ、プロトタイプが
nullのオブジェクトです。
これらの仕様はMDNのObject.groupBy()リファレンスに記載されています。
コールバックが決めるのは所属先
コールバックは要素を加工して返す関数ではありません。ticket => ticket.teamと書くと、返された担当名が分類先となり、グループの配列には元の問い合わせが入ります。
担当が未設定ならunassignedを返す、といった分類ルールも書けます。本記事では、担当が文字列で、前後の空白を除いても文字が残る場合だけ担当名を採用します。それ以外は未割り当てに分類します。
自己完結したコードを実行する
次のコードは、そのまま一式をコピーして利用できます。問い合わせ4件を分類し、グループごとのIDと件数を表示します。
(() => {
if (typeof Object.groupBy !== "function") {
console.log("この環境はObject.groupBy()に対応していません。");
return;
}
const tickets = [
{ id: "T-101", team: "dev", title: "検索結果の修正" },
{ id: "T-102", team: "support", title: "利用方法の案内" },
{ id: "T-103", team: "dev", title: "表示速度の改善" },
{ id: "T-104", team: null, title: "担当の確認" }
];
const groups = Object.groupBy(tickets, (ticket) => {
const team = typeof ticket.team === "string"
? ticket.team.trim()
: "";
return team || "unassigned";
});
const displayOrder = ["dev", "support", "unassigned"];
for (const team of displayOrder) {
const members = groups[team] ?? [];
const ids = members.map((ticket) => ticket.id).join(", ");
console.log(`${team}: ${ids} (${members.length}件)`);
}
console.log(`元の件数: ${tickets.length}`);
console.log(`salesの件数: ${(groups.sales ?? []).length}`);
console.log(`プロトタイプはnull: ${Object.getPrototypeOf(groups) === null}`);
})();即時実行関数で囲んでいるため、ブラウザーのコンソールで繰り返し実行しても、同じ名前の変数宣言が衝突しにくい構成です。
ブラウザーでの実行方法
対応ブラウザーで通常のWebページを開き、開発者ツールのConsoleにコード全体を入力して実行します。Chrome・Edgeではメニューから開発者ツールを開き、Consoleを選べます。
このコードはページの内容を変更しません。コンソールが評価結果として最後に表示するundefinedは、ログとは別の表示です。
Node.jsでの実行方法
Node.js 24系をインストールした環境で、コード全体をUTF-8のgroup-tickets.cjsとして保存します。ターミナルで保存先のフォルダーを開き、次を実行してください。
node --version
node group-tickets.cjs最初のコマンドでバージョンを確認し、次のコマンドでファイルを実行します。パッケージの追加やpackage.jsonの作成は不要です。
期待される出力
対応環境では、両方の実行方法で次のログになります。
dev: T-101, T-103 (2件)
support: T-102 (1件)
unassigned: T-104 (1件)
元の件数: 4
salesの件数: 0
プロトタイプはnull: trueT-101とT-103は同じ担当なので、同じ配列に入ります。salesに該当する問い合わせはなく、そのプロパティは作られません。そこで?? []を使い、存在しないグループを空配列として扱っています。
表示順はdisplayOrderで明示しています。この例は担当の種類が固定された画面向けです。実データに別の担当名が増える場合は、表示対象も更新するか、生成されたキーを列挙する処理に変更してください。
失敗例と押さえておきたい制約
分類結果は独立したコピーではない
分類先の配列は新しく作られますが、中のオブジェクトは元データと共有されます。MDNも、要素が深くコピーされないことを説明しています。
例えば、メインのサンプルでgroups.dev[0].titleを書き換えると、tickets[0].titleも変わります。分類後の表示データを編集する設計では、元の状態まで変更しないか確認してください。分類するだけなら、このサンプルのコールバックは元データを変更しません。
戻り値には継承されたメソッドがない
プロトタイプがnullなので、groups.hasOwnProperty("dev")のような呼び方は使えません。グループの存在確認にはObject.hasOwn(groups, "dev")、取り出し時の既定値にはサンプルのような?? []が使えます。
また、コールバックの値が数値の1でも文字列の"1"でも、キーは同じ文字列になります。分類キーは、用途に合う文字列へ明示的にそろえると意図が伝わります。
不正な入力は例外になる
nullのように反復できない値を渡すと、分類結果ではなく例外が発生します。次のコードは対応環境で単独実行できる失敗例です。
(() => {
if (typeof Object.groupBy !== "function") {
console.log("Object.groupBy()未対応");
return;
}
try {
Object.groupBy(null, (value) => value);
} catch (error) {
console.log(error instanceof TypeError);
}
})();期待出力はtrueです。例外メッセージの文面は環境により異なるため、ここでは例外の種類だけを確認します。コールバック内で例外を投げた場合も呼び出し全体が失敗し、完成した分類結果は返りません。
外部から受け取ったデータでは、配列であることに加え、各要素が想定するオブジェクトかも確認してください。メインの例は担当の未設定には対応しますが、問い合わせ自体がnullという入力までは扱っていません。
非同期コールバックの完了は待たない
このAPIは同期処理です。async関数をコールバックに渡しても、その完了を待って分類してくれるわけではありません。通常のPromiseがキーとして文字列化され、意図しないグループにまとめられる原因になります。
担当名の取得に通信が必要なら、先に非同期処理を完了させ、担当名を持つ配列を作ってから分類します。通信エラーもその段階で扱ってください。サンプルには非同期処理がなく、未処理のPromise拒否は発生しません。
活用事例と導入の判断
担当別の問い合わせ一覧のほか、記事のカテゴリ別表示や、注文の状態別表示にも使えます。分類キーを返す部分を業務ルールに合わせれば、後続の表示処理を整理できます。
実際に導入するときは、未設定値の扱い、表示対象と順序、分類後にデータを編集するかを先に決めると、実装が安定します。特に未設定を単に欠落として扱うと、対応すべき問い合わせを画面から見落とすため、この例では専用のグループを用意しました。
全件を処理してグループの配列を保持するので、大量データでは処理時間とメモリーも確認してください。件数だけが必要な処理なら、要素の配列をすべて保持する設計が適切か検討します。
公式参考資料
- MDN:Object.groupBy()の引数・戻り値・参照共有
- web.dev:2024年3月のWebプラットフォーム更新と対応ブラウザー
- Node.js:21で導入された配列グループ化
- Node.js:リリースとサポート状況
掲載コードと期待出力は、公式記述に照らして構文と処理順を机上確認したものです。ブラウザーやNode.jsでの実機実行は行っていません。
Googleの優先するニュース提供元にSEの部屋を追加
Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。
候補にSEの部屋が表示されない場合は、まだ追加できません。