順番に読み進めながら学べます

Map と Set — 重複排除とキー付き集計

同じ値を 1 つしか持たない Set と、キーと値の組を順番どおりに持つ Map を扱います。重複の取り除き方、追加と削除、キーで値を引く書き方、Object との使い分けまで進みます。

商品に付いたタグを配列で集めると同じタグが何度も混ざり、実際に何種類あるかを知るには includes で毎回確かめる手間が要ります。商品 ID ごとの在庫数のようにキーと値の組を持ちたいときも、配列だけでは ID と数量を 2 本の配列に分けて対応づける必要があり、オブジェクトのキーは文字列と symbol に限られるため数値をそのままキーにはできません。

この記事では、重複を持たない Set と、文字列以外もキーにできる Map を扱います。

重複を取り除く — Set の作り方

タグ付きの商品一覧から、実際に使われているタグが何種類あるかを知りたいとします。配列のままでは同じタグが複数回並ぶため、そのまま数えると件数が水増しされます。重複をあらかじめ取り除いて持てるデータ構造が必要です。

Set(同じ値を 1 つしか持たないデータ構造)は new Set(tags) のように配列を渡して作ります。new を先頭に付けると、Set や Map のような決まった種類のデータ構造を新しく 1 つ作るという意味になります。

同じ値が来ても増えないので、size を読むだけで種類の数が分かります。同じ値かどうかは === と同じ基準で判定されます。

Set は配列ではないため、uniqueTags[0] のように index を指定しても読み出せません。配列として扱いたいときは [...uniqueTags] のようにスプレッド構文で変換します。

並び順は追加された順のまま保たれるので、元の配列に現れた順序がそのまま残ります。

Set は同じ値を 1 つにまとめる
1 件目セールまだ持っていない1 件目として入る2 件目新着まだ持っていない2 件目として入る3 件目セールすでに持っている件数は増えない
すでに持っている値が来たときは何も起きません。追加された順序はそのまま残ります
const tags = ["セール", "新着", "セール", "送料無料"];

// 配列を渡すと、重複を取り除いた Set ができる
const uniqueTags = new Set(tags);
console.log(uniqueTags.size);              // 3

// スプレッド構文で配列に戻す(追加された順のまま)
console.log([...uniqueTags].join(", "));   // セール, 新着, 送料無料

// 元の配列はそのまま
console.log(tags.length);                  // 4

// 空の Set から作り始めることもできる
const empty = new Set();
console.log(empty.size);                   // 0

商品に付いたタグの配列から、実際に使われているタグの種類を取り出します。tags は宣言済みです。

① 元の配列の件数を表示してください。

② 重複を取り除いたデータ構造を作り、その件数を表示してください。

③ 重複を取り除いた結果を配列に戻して、「, 」でつないで表示してください。

④ ③ の配列の 2 番目を表示して、並び順が保たれていることを確かめてください。

(正しく実行できれば解説が表示されます)

JavaScript / TypeScript エディタ

コードを実行してください

値を追加・削除する — add と has と delete

訪問したユーザー ID を記録しておき、同じ人が何度来ても 1 人として数えたい場面があります。配列で持つと、追加のたびに includes で調べてから push する必要があり、記録件数が多いほど確認に時間がかかります。

Set には値を足す add、持っているかを調べる has、取り除く delete、件数を読む size があります。add は同じ値が来ても何も起こらないので、追加前に確認する必要がありません。

hastruefalse を返し、delete は取り除けたかどうかを返します。

add と delete で size がどう動くか
add で新しい IDまだ持っていないsize が1 増えるadd で同じ IDすでに持っているsize は変わらないdelete でID を指定記録から取り除くsize が1 減る
同じ値を何度 add しても件数は増えません。取り除くときだけ件数が減ります
const visitors = new Set(["u-102", "u-337"]);

// 持っているかを調べる
console.log(visitors.has("u-102"));   // true
console.log(visitors.has("u-999"));   // false

// 足す。同じ値なら件数は変わらない
visitors.add("u-541");
console.log(visitors.size);           // 3
visitors.add("u-541");
console.log(visitors.size);           // 3

// 取り除く。取り除けたかどうかが返る
console.log(visitors.delete("u-102"));  // true
console.log(visitors.delete("u-102"));  // false(もう無い)
console.log(visitors.size);             // 2

その日の訪問ユーザーの記録を操作して、人数の変化を確かめます。visitors は宣言済みです。

u-337 が記録済みかどうかを表示してください。

u-999 を記録して、件数を表示してください。

③ 同じ u-999 をもう一度記録して、件数を表示してください。

u-102 を記録から取り除いて、まだ残っているかどうかを表示してください。

JavaScript / TypeScript エディタ

コードを実行してください

キーと値の組を持つ — Map

商品 ID ごとの在庫数のように、キーと値を組にして持ちたい場面があります。オブジェクトでも実現できますが、ID のように実行時に決まるキーを集計したり、あとから増減させたりする用途には、専用のメソッドを持つデータ構造のほうが向いています。

Map(キーと値の組を登録した順に持つデータ構造)は new Map() で作り、set(キー, 値) で登録します。読むときは get(キー)、あるかどうかは has(キー)、件数は size です。

同じキーで set をやり直すと値が上書きされます。最初から中身を入れるときは new Map([["P-001", 12], ["P-002", 5]]) のように、組の配列を渡します。

Map はキーで組を出し入れする
set でP-001 と 12組として登録するsize が1 増えるget でP-001 を指定キーを探す値の 12が返るget でP-999 を指定見つからないundefinedが返る
set で組を登録し、get でキーから値を引きます。登録が無いキーは undefined が返ります
const stock = new Map();

// set で組を登録する
stock.set("P-001", 12);
stock.set("P-002", 5);
console.log(stock.size);            // 2

// get でキーから値を引く
console.log(stock.get("P-002"));    // 5
console.log(stock.get("P-999"));    // undefined
console.log(stock.has("P-999"));    // false

// 同じキーで set し直すと上書きされる
stock.set("P-001", 8);
console.log(stock.get("P-001"));    // 8
console.log(stock.size);            // 2(件数は増えない)

// 最初から組を入れて作ることもできる
const prices = new Map([["P-001", 3980], ["P-002", 2480]]);
console.log(prices.get("P-001"));   // 3980

Map の値はドット記法では読めない

stock.get("P-001") のかわりに stock["P-001"] と書いても値は返りません。この書き方は Map 自身のプロパティを探しにいくため、登録した組とは無関係で undefined が返ります。読み書きは getset を通します。

商品 ID をキーにして在庫数を持つデータ構造を組み立てます。stock は空の状態で宣言済みです。

P-001 を 12、P-002 を 5、P-003 を 3 として登録し、件数を表示してください。

P-002 の在庫数を表示してください。

③ 登録していない P-999 の在庫数を表示してください。

P-001 の在庫を 4 に更新して、更新後の値を表示してください。

JavaScript / TypeScript エディタ

コードを実行してください

Object との使い分け — キーの型と並び順

キーと値を持つという点ではオブジェクトと同じなので、どちらで書くかを選びます。設定項目のようにキーがコードを書く時点で決まっているならオブジェクト、ID や日付のように実行時に増えていくキーを集計するなら Map が扱いやすくなります。

判断の材料になるのが、キーの型と並び順です。

オブジェクトのキーは文字列に変換されるので、record[2026] と書いても実際のキーは "2026" です。Map は渡した値をそのままキーにするため、数値の 2026 と文字列の "2026" は別のキーとして扱われます。

並び順も、Map は登録した順をそのまま保ちます。

Map はキーの型をそのまま保つ
Map に数値 2026 で登録数値のままキーになるget(2026)で取れる同じ Map を文字列で引く別のキー扱いになるundefinedが返るObject に数値 2026 で登録文字列に変換される文字列でも取れてしまう
Map は数値の 2026 と文字列の "2026"別のキーとして区別 します。オブジェクトは文字列に変換します。
観点ObjectMap
キーに使える型文字列に変換されるどの型でもそのまま
並び順数字のキーが先に並ぶ登録した順のまま
件数の数え方Object.keys の lengthsize
値の読み書きobj.key / obj[key]get と set
向いている場面決まった項目の集まり実行時に増えるキーの集計

Map の中身を一覧するときは [...stock.entries()] と書いて、[キー, 値] の 2 要素の配列が並んだ配列に直します。こうすると entries[0] のように 1 組ずつ取り出せます。組の数ぶん同じ処理を繰り返す書き方は、繰り返しを学ぶ記事であらためて扱います

キーだけが必要なときは stock.keys() を使いますが、これが返すのは配列ではないので join を直接つなげられません。[...stock.keys()] と書いて配列にしてから使います。

const scores = new Map([["math", 80]]);

// Map は数値のキーを数値のまま持つ
scores.set(2026, "今年の集計");
console.log(scores.get(2026));               // 今年の集計
console.log(scores.get("2026"));             // undefined(別のキー)

// オブジェクトはキーが文字列に変換される
const record = {};
record[2026] = "今年の集計";
console.log(Object.keys(record)[0]);         // 2026
console.log(typeof Object.keys(record)[0]);  // string
console.log(record["2026"]);                 // 今年の集計(同じキー扱い)

// keys() / entries() はスプレッド構文で配列にしてから使う
console.log([...scores.keys()].join(", "));   // math, 2026

const pairs = [...scores.entries()];
console.log(pairs[0].join(": "));             // math: 80

// オブジェクトは整数のようなキーが先に並ぶ
const ranking = {};
ranking[3] = "c";
ranking[1] = "a";
console.log(Object.keys(ranking).join(", "));  // 1, 3

在庫の Map を一覧表示してから、キーの型による違いを確かめます。stock は 3 組を登録した状態で宣言済みです。

① 登録されているキーだけを配列にして、「, 」でつないで表示してください。

② 3 組を [キー, 値] の配列に直し、1 組ずつ「キー: 値」の形にして 3 行で表示してください。

③ 数値の 2026 をキーにして「年間ランキング」を登録し、そのキーで取り出して表示してください。

④ 同じ値を文字列の "2026" で取り出して、返ってきた値を表示してください。

JavaScript / TypeScript エディタ

コードを実行してください
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1const ids = new Set(["u-1", "u-2"]); のあとに ids.add("u-1"); を実行すると、ids.size はいくつですか?

Q2stock が Map のとき、登録していないキーを stock.get("P-999") で引くと何が返りますか?

Q3数値の 2026 をキーとして登録したとき、Map とオブジェクトで違うのはどこですか?