JavaScript | 配列・オブジェクト:実務パターン – ID で検索

JavaScript JavaScript
スポンサーリンク

ID で検索とは何か

「ID で検索」は、配列やオブジェクトの中から“特定の一件”を高速・安全に取り出す実務の基本パターンです。ここが重要です:配列から毎回探すのは線形検索で遅くなりがち。頻繁に検索するなら辞書(オブジェクト/Map)に変換しておき、ID→レコードを即座に取り出す設計にすると安定します。

// 配列で単発検索(O(n))
const user = users.find(u => u.id === targetId);

// 辞書で検索(O(1))
const user = usersById[targetId];       // もしくは map.get(targetId)
JavaScript

配列から ID で探す(単発検索の定番)

基本:find / findIndex

const users = [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }];
const targetId = 2;

const user = users.find(u => u.id === targetId);      // レコード(見つからなければ undefined)
const index = users.findIndex(u => u.id === targetId); // 位置(見つからなければ -1)
JavaScript
  • 使い分け: レコードが欲しいときは find。位置が欲しい(更新・削除)ときは findIndex。
  • 安全策: 比較は厳密等価(===)で型ブレを防ぐ。文字列IDなら String(u.id) === String(targetId) のように揃える。

その場で非破壊更新(ID マッチだけ置換)

function updateById(items, id, patch) {
  return items.map(it => it.id === id ? { ...it, ...patch } : it);
}
JavaScript
  • ポイント: map で“該当IDだけ新インスタンス”に置換。イミュータブルで差分検知が安定。

辞書(オブジェクト/Map)にして O(1) で検索

オブジェクトを辞書化(Record パターン)

const usersById = Object.fromEntries(users.map(u => [u.id, u]));
// 取得
const user = usersById[targetId];
JavaScript
  • 利点: シンプルで速い、JSON シリアライズもしやすい。
  • 注意: キーは文字列化されるため、数値IDでも "2" として格納される。型を揃える習慣を持つ。

Map を使う(キー型を維持、メソッドが豊富)

const usersMap = new Map(users.map(u => [u.id, u]));
const user = usersMap.get(targetId);
JavaScript
  • 利点: キー型を保持(数値/オブジェクトキー)、サイズや反復が便利(.size, .keys())。
  • 注意: JSON 化は直接できない。必要なら Array.from(map) で配列化して保存。

実務の分岐

  • 少量・保存優先: オブジェクト辞書。
  • 大量・キー多様・操作豊富: Map。

正規化(entities)で複数テーブルを整える

正規化の骨子(ID 指向に再設計)

const entities = {
  users: { 1: { id: 1, name: "Alice" }, 2: { id: 2, name: "Bob" } },
  posts: { 10: { id: 10, authorId: 1, commentIds: [100] } },
  comments: { 100: { id: 100, authorId: 2, text: "Hi" } }
};

// 検索
const user = entities.users[1];
const post = entities.posts[10];
JavaScript
  • メリット: 一元更新(users[1] を更新すれば全参照が最新)、部分更新が高速、関係が明確。
  • 表示時の復元: ID を辿って“見せる形”にデノーマライズして使う。

まとめ検索・一括取得・存在チェック

複数 ID をまとめて取得

function pickMany(dict, ids) {
  return ids.map(id => dict[id]).filter(x => x != null);
}
const users = pickMany(usersById, [1, 2, 99]); // 99 は除外
JavaScript

存在チェックと既定値

const exists = Object.hasOwn(usersById, targetId); // 自前キーの判定
const user = usersById[targetId] ?? { id: targetId, name: "(unknown)" };
JavaScript

部分一致の検索(辞書+インデックスの併用)

  • ラベル検索: 事前に name→id のインデックスを組むと速い。
const indexByName = Object.fromEntries(users.map(u => [u.name.toLowerCase(), u.id]));
const id = indexByName["alice"];
const user = id != null ? usersById[id] : undefined;
JavaScript

更新・削除の実務パターン(ID 基軸で安全に)

辞書の非破壊更新

function upsert(dict, record) {
  return { ...dict, [record.id]: { ...(dict[record.id] ?? {}), ...record } };
}
function remove(dict, id) {
  const { [id]: _, ...rest } = dict;
  return rest;
}
JavaScript
  • ポイント: 追加/更新は上書き(アップサート)、削除は分割代入でキー除去。すべて非破壊。

配列+辞書のハイブリッド

function upsertList(list, record) {
  const i = list.findIndex(x => x.id === record.id);
  if (i === -1) return [...list, record];
  return list.map((x, idx) => (idx === i ? { ...x, ...record } : x));
}
JavaScript
  • ポイント: “表示順(配列)”と“即時検索(辞書)”を両立する場合に併用。

重要な注意点(ここが核心)

ID の型と一意性

  • 型の統一: 指針: 受け取り次第 String(id) などで正規化して保管。比較は常に厳密等価(===)。
  • 一意性の保証: 指針: サーバー発行IDや UUID を使用。インデックス番号は避ける(並び替えで破綻)。

欠損・安全なアクセス

  • 欠損対策: 指針: dict[id] ?? default で既定値。配列探索は find(...) ?? null
  • ネスト: 指針: entities.post?.[id]?.commentIds ?? [] のように ?.?? を組み合わせる。

性能と同期

  • O(n) の限界: 指針: 検索が多い・データが増えるなら辞書化(O(1))に切り替える。
  • 同期の一貫性: 指針: 正規化側(辞書)を唯一の更新源にし、表示は再構成。二重更新を避ける。

実践レシピ(すぐ使えるコード)

辞書を作るユーティリティ

const toDict = (list, key = "id") =>
  Object.fromEntries(list.map(x => [x[key], x]));
JavaScript

一括検索+欠損補完

function lookupAll(dict, ids, filler = id => ({ id, missing: true })) {
  return ids.map(id => dict[id] ?? filler(id));
}
JavaScript

参照整合付き追加(関係を更新)

function addComment(entities, comment) {
  const comments = { ...entities.comments, [comment.id]: comment };
  const post = entities.posts[comment.postId];
  const nextPost = { ...post, commentIds: [...post.commentIds, comment.id] };
  return { 
    ...entities, 
    comments, 
    posts: { ...entities.posts, [post.id]: nextPost } 
  };
}
JavaScript

まとめ

ID で検索は「単発なら find、頻繁なら辞書化」という切り替えが核心です。辞書(オブジェクト/Map)にして O(1) 検索へ、正規化(entities)で“一元更新”できる構造を作る。更新・削除は非破壊に、関係データは両側の整合を保つ。ID の型と一意性を徹底し、欠損には ?.?? で安全化。これだけ押さえれば、初心者でも大規模データを意図どおりに素早く、安全に検索・更新できます。

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