JavaScript Tips | 配列ユーティリティ:マップ生成

JavaScript JavaScript
スポンサーリンク

何をしたいユーティリティか:「マップ生成」

ここでの「マップ生成」は、配列から「キー → 値」の対応表(オブジェクトや Map)を作る処理のことです。
「id からユーザーを引けるようにしたい」「コードからラベルを引けるようにしたい」といったときに使います。

配列のままだと「毎回 find で探す」ことになりがちですが、
一度マップを作っておけば、map.get(id) のように一発で取り出せるようになります。


基本形:オブジェクトでマップを作る(キーは文字列前提)

シンプルな key → item のマップ

まずは一番よくある、「id から要素を引けるマップ」をオブジェクトで作るユーティリティです。

function toMapById(array, keyProp = "id") {
  if (!Array.isArray(array)) {
    return {};
  }

  const map = {};
  for (const item of array) {
    if (item && item[keyProp] != null) {
      const key = String(item[keyProp]);
      map[key] = item;
    }
  }
  return map;
}
JavaScript

ここでの重要ポイントをかみ砕きます。

配列でなければ空オブジェクトを返す(安全側)。
keyProp(デフォルトは "id")でキーにするプロパティを指定できる。
null / undefined なキーはスキップする(壊れたキーを入れない)。
キーは String(...) で文字列化してから使う(オブジェクトのキーは文字列だから)。

これで、「id → 要素」のマップが作れます。

動作例

const users = [
  { id: 1, name: "Taro" },
  { id: 2, name: "Hanako" },
];

const userMap = toMapById(users);

userMap["1"]; // { id: 1, name: "Taro" }
userMap["2"]; // { id: 2, name: "Hanako" }
JavaScript

数値の id でも、内部では "1" のような文字列キーとして扱われます。
実務では「API から来る id は文字列」のことも多いので、どちらでも動くようにしておくと安心です。


汎用版:キーと値を関数で指定できる toMap

keySelector / valueSelector を受け取る

もう少し柔軟に、「キーも値も自分で決めたい」場合のユーティリティです。

function toMap(array, keySelector, valueSelector = (x) => x) {
  if (!Array.isArray(array)) {
    return {};
  }
  if (typeof keySelector !== "function") {
    return {};
  }

  const map = {};
  array.forEach((item, index) => {
    const rawKey = keySelector(item, index);
    if (rawKey == null) {
      return;
    }
    const key = String(rawKey);
    const value = valueSelector(item, index);
    map[key] = value;
  });

  return map;
}
JavaScript

重要ポイントはここです。

keySelector で「キーにする値」を決める。
valueSelector で「マップに入れる値」を決める(省略時は元の要素)。
キーが null / undefined のものはスキップする。
キーは文字列化してからオブジェクトに入れる。

これで、「id → item」「code → label」「id → name」など、いろいろなマップを作れます。

動作例1:id → ユーザー

const userMap = toMap(users, (u) => u.id);
// { "1": {…}, "2": {…} }
JavaScript

動作例2:コード → ラベル

const statuses = [
  { code: "A", label: "有効" },
  { code: "D", label: "削除済み" },
];

const statusLabelMap = toMap(
  statuses,
  (s) => s.code,
  (s) => s.label
);

// statusLabelMap["A"] === "有効"
// statusLabelMap["D"] === "削除済み"
JavaScript

「コードからラベルを引く」という、業務で超よくあるパターンが一行で書けます。


Map オブジェクト版:キーにオブジェクトや数値をそのまま使いたいとき

なぜ Map を使うのか

オブジェクトのキーは文字列(とシンボル)に限られますが、
Map なら「数値」「オブジェクト」「配列」などもそのままキーにできます。

「数値のままキーにしたい」「オブジェクトをキーにしたい」ような場面では、Map 版のユーティリティが便利です。

toKeyedMap

function toKeyedMap(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;
}
JavaScript

ここでは、キーを文字列化せず、そのまま map.set(key, value) しています。
Map なので、map.get(key) で同じキーを渡せば値が取れます。

動作例:数値キーの Map

const userMap = toKeyedMap(users, (u) => u.id);

userMap.get(1); // { id: 1, name: "Taro" }
userMap.get(2); // { id: 2, name: "Hanako" }
JavaScript

オブジェクトキーを使うような高度なケースは、初心者のうちはあまり出てこないので、
まずは「数値キーをそのまま使える」くらいのイメージで大丈夫です。


なぜ「マップ生成」が業務で強いのか

探索コストが劇的に下がる

配列のまま「id で探す」と、毎回 find になります。

const user = users.find((u) => u.id === targetId);
JavaScript

これは要素数が増えるほど遅くなります(1 件ずつ見ていくから)。
一方、マップにしておけば、

const user = userMap[targetId];      // オブジェクト版
// または
const user = userMap.get(targetId);  // Map 版
JavaScript

で一発です。
「何度も同じキーで引く」処理があるなら、マップ生成はほぼ必須テクニックです。

コードの意図がはっきりする

toMapByIdtoMap を通すことで、「これは“id から引くためのマップ”なんだな」と一目で分かります。

配列のまま find を書いていると、
「これはたまたま一回だけ探しているのか」「実は何度も呼ばれていて重いのか」が見えづらいですが、
マップにしておけば「ここで索引用の構造を作っている」と明示できます。


手を動かして感覚をつかむ

ブラウザのコンソールなどで、次のように試してみてください。

function toMapById(array, keyProp = "id") {
  if (!Array.isArray(array)) return {};
  const map = {};
  for (const item of array) {
    if (item && item[keyProp] != null) {
      const key = String(item[keyProp]);
      map[key] = item;
    }
  }
  return map;
}

function toMap(array, keySelector, valueSelector = (x) => x) {
  if (!Array.isArray(array) || typeof keySelector !== "function") return {};
  const map = {};
  array.forEach((item, index) => {
    const rawKey = keySelector(item, index);
    if (rawKey == null) return;
    const key = String(rawKey);
    const value = valueSelector(item, index);
    map[key] = value;
  });
  return map;
}

const users = [
  { id: 1, name: "Taro" },
  { id: 2, name: "Hanako" },
];

const userMap1 = toMapById(users);
const userMap2 = toMap(users, (u) => u.id, (u) => u.name);
JavaScript

userMap1["1"]userMap2["2"] を触ってみて、「配列からマップに変わると、どうアクセスしやすくなるか」を体感してみてください。

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

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

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

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