---
type: how-to
language: javascript
slug: array/group
title: "配列をキーごとにまとめる（グループ化）"
title_tag: "JavaScript 配列のグループ化 — groupBy と reduce の書き方"
summary: >
  Object.groupBy が使えるならそれが最短です。使えない環境向けの reduce での書き方、
  キーが数値や undefined になる罠、件数だけ数える形までまとめます。
description: >
  Object.groupBy と Map.groupBy の違い、reduce で書く従来の形、キーが文字列に変換される落とし穴。件数を数える、条件で二分するなど、よく使う形をまとめています。
status: published
difficulty: 2
minutes: 8

versions:
  verified: "Node 22.22.3"
  since: "ES2024"
  deprecated: null
  removed: null

sources:
  - title: "Object.groupBy — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-object.groupby"
  - title: "Map.groupBy — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-map.groupby"
  - title: "Object.groupBy() — MDN"
    url: "https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy"

terms: [グループ化, 累積値, キー]

links:
  related:
    - javascript/reference/array/reduce
    - javascript/reference/object/keys
    - javascript/learn/object/iterate

content_updated_at: 2026-09-07
published_at: 2026-09-07
---

## 答え

`Object.groupBy()` に「何をキーにするか」を渡します。

```js run
const users = [
  { name: 'あかり', team: 'A' },
  { name: 'ゆうと', team: 'B' },
  { name: 'はると', team: 'A' },
];

console.log(Object.groupBy(users, u => u.team));
```
```output
{ A: [ { name: 'あかり', team: 'A' }, { name: 'はると', team: 'A' } ], B: [ { name: 'ゆうと', team: 'B' } ] }
```

**ES2024 です。**（Node 21 / Chrome 117 / Safari 17.4 以降）
古い環境も相手にするなら、後述の `reduce()` で書きます。

上のコードは書き換えて実行できます。`u.team` を `u.name.length` にしてみてください。

## キーは必ず文字列になる

`Object.groupBy()` の戻り値は**オブジェクト**なので、キーは[key:文字列に変換されます]。

```js run
const nums = [1, 2, 3, 4, 5, 6];

const byMod = Object.groupBy(nums, n => n % 3);

console.log(byMod);
console.log(Object.keys(byMod));
```
```output
{ '0': [ 3, 6 ], '1': [ 1, 4 ], '2': [ 2, 5 ] }
[ '0', '1', '2' ]
```

数値でキーを引きたいときは `Map.groupBy()` を使います。**キーの型が保たれます。**

```js run
const nums = [1, 2, 3, 4, 5, 6];

const byMod = Map.groupBy(nums, n => n % 3);

console.log(byMod);
console.log(byMod.get(0));
console.log(byMod.get('0'));
```
```output
Map(3) { 1 => [ 1, 4 ], 2 => [ 2, 5 ], 0 => [ 3, 6 ] }
[ 3, 6 ]
undefined
```

`Object.groupBy()` なら `'0'`、`Map.groupBy()` なら `0` で引きます。
**取り違えると `undefined` になります。**

| | 戻り値 | キーの型 | 順序 |
|---|---|---|---|
| `Object.groupBy()` | オブジェクト | [bad:文字列になる] | 数字に見えるキーが先 |
| `Map.groupBy()` | `Map` | [em:そのまま] | [em:出てきた順] |

オブジェクトをキーにしたいなら `Map.groupBy()` しかありません。

## 古い環境で書く

`reduce()` で同じことができます。**中身は「無ければ配列を作って、押し込む」だけ**です。

```js run
const users = [
  { name: 'あかり', team: 'A' },
  { name: 'ゆうと', team: 'B' },
  { name: 'はると', team: 'A' },
];

const byTeam = users.reduce((acc, u) => {
  (acc[u.team] ??= []).push(u);
  return acc;
}, {});

console.log(Object.keys(byTeam));
console.log(byTeam.A.map(u => u.name));
```
```output
[ 'A', 'B' ]
[ 'あかり', 'はると' ]
```

`??=` は「[key:まだ無ければ入れる]」です。書けない環境なら、こう書きます。

```js run
const users = [{ name: 'あかり', team: 'A' }, { name: 'ゆうと', team: 'A' }];

const byTeam = users.reduce((acc, u) => {
  if (!acc[u.team]) acc[u.team] = [];
  acc[u.team].push(u);
  return acc;
}, {});

console.log(byTeam.A.length);
```
```output
2
```

**`{ ...acc }` で作り直さないでください。** 要素の数だけ複製することになります。
理由は [Array.prototype.reduce()](/ja/javascript/reference/array/reduce/) にあります。

## キーが undefined になる

キーを取り出す式が空振りすると、**`undefined` という名前のグループ**ができます。

```js bad
const users = [
  { name: 'あかり', team: 'A' },
  { name: 'ゆうと' },
];

const byTeam = Object.groupBy(users, u => u.team);

console.log(Object.keys(byTeam));
console.log(byTeam.undefined.length);
```
```output
[ 'A', 'undefined' ]
1
```

**文字列の `'undefined'` です。** 気づかずに集計に混ざります。

**既定値を決めるか、先に弾いてください。**

```js run
const users = [
  { name: 'あかり', team: 'A' },
  { name: 'ゆうと' },
];

console.log(Object.keys(Object.groupBy(users, u => u.team ?? '未所属')));
console.log(Object.keys(Object.groupBy(users.filter(u => u.team), u => u.team)));
```
```output
[ 'A', '未所属' ]
[ 'A' ]
```

「所属が無い人」を[key:見せたいのか、捨てたいのか]を決めてから書きます。

## 件数だけ数える

グループの中身が要らないなら、配列を作らず数えるほうが安いです。

```js run
const votes = ['りんご', 'みかん', 'りんご', 'ぶどう', 'りんご'];

const count = votes.reduce((acc, v) => {
  acc[v] = (acc[v] ?? 0) + 1;
  return acc;
}, {});

console.log(count);
```
```output
{ 'りんご': 3, 'みかん': 1, 'ぶどう': 1 }
```

`groupBy` で作ってから数えることもできます。**読みやすいほうを選んでください。**

```js run
const votes = ['りんご', 'みかん', 'りんご'];

const count = Object.fromEntries(
  Object.entries(Object.groupBy(votes, v => v)).map(([k, list]) => [k, list.length]),
);

console.log(count);
```
```output
{ 'りんご': 2, 'みかん': 1 }
```

## グループごとに合計する

グループ化してから、各グループを `reduce()` します。

```js run
const sales = [
  { shop: '東京', amount: 1200 },
  { shop: '大阪', amount: 800 },
  { shop: '東京', amount: 300 },
];

const byShop = Object.groupBy(sales, s => s.shop);

const totals = Object.fromEntries(
  Object.entries(byShop).map(([shop, list]) => [
    shop,
    list.reduce((sum, s) => sum + s.amount, 0),
  ]),
);

console.log(totals);
```
```output
{ '東京': 1500, '大阪': 800 }
```

合計の落とし穴（小数・空配列・文字列が混ざる）は
[配列の合計を出す](/ja/javascript/how-to/array/sum/) にまとめてあります。

## 複数のキーでまとめる

キーを繋いだ文字列にします。**区切りには、値に出てこない文字を選んでください。**

```js run
const rows = [
  { year: 2026, month: 9, n: 1 },
  { year: 2026, month: 9, n: 2 },
  { year: 2026, month: 8, n: 3 },
];

const byMonth = Object.groupBy(rows, r => `${r.year}-${r.month}`);

console.log(Object.keys(byMonth));
console.log(byMonth['2026-9'].map(r => r.n));
```
```output
[ '2026-9', '2026-8' ]
[ 1, 2 ]
```

[dim:'2026-9' が先に来ています。数字だけのキーなら小さい順に並びますが、'2026-9' は数字ではないので出てきた順です。ここでも順序に頼らないでください。]

[dim:キーに使う値そのものに区切り文字が入りうるなら、この方法は使えません（'a-b' と 'a' + '-b' が衝突します）。そのときは Map.groupBy と入れ子にしてください。]
