JavaScript Tips | 配列ユーティリティ:Map 変換

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「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 → user
  • id → name
  • name → 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 キーをスキップする理由

keySelectornullundefined を返したとき、そのまま 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;
JavaScript

idToUser.get(1)idToName.get(2) の結果を見て、
「配列から Map に変わると、どうアクセスしやすくなるか」を体感してみてください。

そのうえで、自分のプロジェクトに

export function toMap(...) { ... }
JavaScript

のような関数を置き、「配列を“キーで引ける形”にしたくなったら、まずこの“Map 変換ユーティリティ”を通す」と決めてみてください。
それだけで、find だらけのコードから、一段スケーラブルで読みやすい設計に近づいていきます。

タイトルとURLをコピーしました