何をしたいユーティリティか:「Map 変換」
ここでの「Map 変換」は、配列から Map(キーと値のペアのコレクション)を作るユーティリティのことです。
「id からユーザーを一発で引きたい」「コードからラベルを引きたい」といったときに使います。
前にやった「マップ生成(オブジェクト版)」と似ていますが、Map を使うことで次のようなメリットがあります。
- キーに数値やオブジェクトをそのまま使える
- 反復順序が挿入順で安定している
- サイズを
map.sizeで簡単に取れる
配列のままだと「毎回 find で探す」ことになりがちですが、
一度 Map に変換しておけば、map.get(key) で一発で取り出せるようになります。
基本形:配列から Map を作る toMap
キーと値を関数で指定する汎用ユーティリティ
まずは、「キーも値も自分で決められる」汎用的な Map 変換ユーティリティからいきます。
function toMap(array, keySelector, valueSelector = (x) => x) {
if (!Array.isArray(array) || typeof keySelector !== "function") {
return new Map();
}
const map = new Map();
array.forEach((item, index) => {
const key = keySelector(item, index);
if (key == null) {
return; // null / undefined キーはスキップ
}
const value = valueSelector(item, index);
map.set(key, value);
});
return map;
}
JavaScriptここでの重要ポイントをかみ砕きます。
keySelectorで「キーにする値」を決めるvalueSelectorで「Map に入れる値」を決める(省略時は元の要素)- キーが
null/undefinedのものはスキップして安全側に倒す Mapを返すので、map.get(key)/map.has(key)/map.sizeが使える
これで、「id → item」「code → label」「id → name」など、いろいろな Map を作れます。
動作例1:id → ユーザー
const users = [
{ id: 1, name: "Taro" },
{ id: 2, name: "Hanako" },
];
const userMap = toMap(users, (u) => u.id);
// Map(2) { 1 => {…}, 2 => {…} }
userMap.get(1); // { id: 1, name: "Taro" }
userMap.get(2); // { id: 2, name: "Hanako" }
JavaScriptここでは「キーは id」「値はユーザーオブジェクトそのもの」という形で Map を作っています。
動作例2:コード → ラベル
const statuses = [
{ code: "A", label: "有効" },
{ code: "D", label: "削除済み" },
];
const statusLabelMap = toMap(
statuses,
(s) => s.code,
(s) => s.label
);
// "A" → "有効"
statusLabelMap.get("A"); // "有効"
statusLabelMap.get("D"); // "削除済み"
JavaScript「コードからラベルを引く」という、業務で超よくあるパターンが一行で書けます。
なぜオブジェクトではなく Map を使うのか
1. キーに数値やオブジェクトをそのまま使える
オブジェクトのキーは基本的に文字列(とシンボル)ですが、Map は次のようなキーもそのまま使えます。
- 数値
- オブジェクト
- 配列
- 関数
例えば、「ユーザーオブジェクトそのものをキーにしたい」ようなケース。
const user1 = { id: 1 };
const user2 = { id: 2 };
const metaMap = new Map();
metaMap.set(user1, { lastLogin: "2024-01-01" });
metaMap.set(user2, { lastLogin: "2024-02-01" });
metaMap.get(user1); // { lastLogin: "2024-01-01" }
JavaScript初心者のうちは「id をキーにする」だけで十分ですが、
「オブジェクトをキーにできる」という感覚を持っておくと、設計の幅が広がります。
2. サイズが簡単に取れる
オブジェクトで「何件あるか」を知るには、Object.keys(obj).length のような書き方が必要ですが、Map なら map.size で一発です。
userMap.size; // 2
JavaScript「件数をよく見る」ような場面では、これだけでもかなり楽になります。
3. 反復順序が挿入順で安定している
Map は「追加した順番」で反復されます。
for (const [key, value] of userMap) {
// 追加した順に出てくる
}
JavaScriptオブジェクトのキー順は仕様的に少しややこしいので、
「順番も大事にしたい」なら Map のほうが安心です。
よくある業務パターンでの Map 変換
例1:id からユーザーを高速に引きたい
配列のまま「id で探す」と、毎回 find になります。
const user = users.find((u) => u.id === targetId);
JavaScriptこれは要素数が増えるほど遅くなります(1 件ずつ見ていくから)。
一方、Map にしておけば、
const userMap = toMap(users, (u) => u.id);
const user = userMap.get(targetId);
JavaScriptで一発です。
「同じ配列に対して、何度も id で引く」処理があるなら、Map 変換はほぼ必須テクニックです。
例2:コードからラベルを引く辞書を作る
const statuses = [
{ code: "A", label: "有効" },
{ code: "D", label: "削除済み" },
];
const statusLabelMap = toMap(
statuses,
(s) => s.code,
(s) => s.label
);
function getStatusLabel(code) {
return statusLabelMap.get(code) ?? "";
}
JavaScript画面表示や CSV 出力などで、「コードをラベルに変換する」処理が何度も出てくるなら、
こういう Map を一度作っておくと、後がとても楽になります。
例3:id → 集計結果の Map を作る
例えば、「ユーザーごとの合計金額」を集計したいケース。
const payments = [
{ userId: 1, amount: 1000 },
{ userId: 2, amount: 2000 },
{ userId: 1, amount: 500 },
];
const totalByUser = new Map();
payments.forEach((p) => {
const current = totalByUser.get(p.userId) ?? 0;
totalByUser.set(p.userId, current + p.amount);
});
// totalByUser.get(1) === 1500
// totalByUser.get(2) === 2000
JavaScriptここではユーティリティというより「Map そのものの使い方」ですが、
「キーごとに値を蓄積する」処理と Map は相性がとても良いです。
より初心者向けに:toMap の設計をもう一段かみ砕く
なぜ keySelector / valueSelector に分けるのか
toMap を
toMap(array, (item) => item.id, (item) => item.name);
JavaScriptのように使うのは、最初は少し回りくどく見えるかもしれません。
でも、ここを分けておくと次のようなメリットがあります。
- 「何をキーにしているか」が一目で分かる
- 「何を値として持っているか」が一目で分かる
- 同じ配列から、違う形の
Mapをいくつも作れる
例えば、同じ users から
id → userid → namename → user
など、いろいろな辞書を簡単に作れます。
const idToUser = toMap(users, (u) => u.id);
const idToName = toMap(users, (u) => u.id, (u) => u.name);
const nameToUser = toMap(users, (u) => u.name);
JavaScript「キーと値を自由に組み替えられる」というのが、toMap の一番おいしいところです。
null / undefined キーをスキップする理由
keySelector が null や undefined を返したとき、そのまま map.set すると、
「よく分からないキー」が混ざってしまいます。
業務的には「キーが取れないデータは壊れていることが多い」ので、
ユーティリティ側でスキップしてしまうほうが安全です。
if (key == null) {
return; // 何もしない(この要素は無視)
}
JavaScriptこうしておくと、「キーがちゃんと取れたものだけが Map に入る」という前提で後続の処理を書けます。
手を動かして感覚をつかむ
ブラウザのコンソールなどで、次のように試してみてください。
function toMap(array, keySelector, valueSelector = (x) => x) {
if (!Array.isArray(array) || typeof keySelector !== "function") {
return new Map();
}
const map = new Map();
array.forEach((item, index) => {
const key = keySelector(item, index);
if (key == null) return;
const value = valueSelector(item, index);
map.set(key, value);
});
return map;
}
const users = [
{ id: 1, name: "Taro" },
{ id: 2, name: "Hanako" },
];
const idToUser = toMap(users, (u) => u.id);
const idToName = toMap(users, (u) => u.id, (u) => u.name);
idToUser.get(1);
idToName.get(2);
idToUser.size;
JavaScriptidToUser.get(1) や idToName.get(2) の結果を見て、
「配列から Map に変わると、どうアクセスしやすくなるか」を体感してみてください。
そのうえで、自分のプロジェクトに
export function toMap(...) { ... }
JavaScriptのような関数を置き、「配列を“キーで引ける形”にしたくなったら、まずこの“Map 変換ユーティリティ”を通す」と決めてみてください。
それだけで、find だらけのコードから、一段スケーラブルで読みやすい設計に近づいていきます。
