JavaScript | 配列・オブジェクト:実務パターン – 一意データの抽出

JavaScript JavaScript
スポンサーリンク

一意データの抽出とは何か

一意データの抽出は「重複している要素を取り除き、同じものが1回だけ現れる集合にする」処理です。ここが重要です:何を“同じ”とみなすかで手法が変わります。プリミティブ(数値・文字列)なら値一致、オブジェクトなら“IDやキーの一致”、複合条件なら“正規化したキー”で比較します。順序の扱い(先に出たものを残すか、後勝ちにするか)も実務上の重要ポイントです。


プリミティブの重複排除(Set が最短)

Set で一意化する

const nums = [1, 2, 2, 3, 1];
const unique = [...new Set(nums)]; // [1, 2, 3]
JavaScript

Set は同じ値を1つにまとめます。ここが重要です:順序は“最初に出現した順”で維持されます。NaN は同値として扱われ、-00 は同一視されます(多くの用途で問題になりません)。

filter+indexOf の古典パターン(理解用)

const unique = nums.filter((v, i, arr) => arr.indexOf(v) === i);
JavaScript

Set より遅くなることが多く、実務では Set を選ぶのが無難です。


オブジェクト配列の重複排除(IDで一意化)

先勝ち(最初に出たものを残す)

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

const seen = new Set();
const unique = users.filter(u => !seen.has(u.id) && seen.add(u.id));
// [{ id:1, name:"Alice" }, { id:2, name:"Bob" }]
JavaScript

ここが重要です:seen.add(...) は戻り値に Set を返すため、短く“チェック+登録”ができます。読みやすさを重視するなら分けて書いても構いません。

後勝ち(最後のものを残す)

const byId = new Map(users.map(u => [u.id, u]));
const unique = [...byId.values()];
// [{ id:1, name:"Alice v2" }, { id:2, name:"Bob" }]
JavaScript

Map は同じキーで上書きされるため、最終出現を採用します。ログや設定の“上書き結果”を得たいときに向きます。


任意のキーで一意化(uniqueBy ユーティリティ)

キー関数で柔軟に指定する

function uniqueBy(list, keyFn, keep = "first") {
  if (keep === "last") {
    const m = new Map(list.map(x => [keyFn(x), x]));
    return [...m.values()];
  }
  const seen = new Set();
  const out = [];
  for (const x of list) {
    const k = keyFn(x);
    if (!seen.has(k)) { seen.add(k); out.push(x); }
  }
  return out;
}

// 使い方
const products = [
  { sku: "A-001", name: "Pen" },
  { sku: "a-001", name: "Pen v2" },
  { sku: "B-010", name: "Book" }
];

const uniqueSku = uniqueBy(products, p => p.sku.toLowerCase(), "first");
JavaScript

ここが重要です:キー関数で“正規化(小文字化・トリム・変換)”を行えば、表記ゆれを吸収できます。実務では「同じ意味の値を同じキーにする」設計が肝心です。


文字列の表記ゆれ・正規化(大小・空白・全角半角)

正規化してから一意化する

const names = ["Alice", "alice ", "ALICE"];
const normalize = s =>
  s.trim().toLowerCase().replace(/[A-Za-z0-9]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 0xFEE0));

const unique = [...new Set(names.map(normalize))]; // ["alice"]
JavaScript

ここが重要です:前処理で“同じものは同じキーへ”寄せると、重複排除の精度が上がります。ドメインに応じて、記号除去・NFC正規化なども検討します。


ネストやグループを跨いだ一意化(全体で重複排除)

グループ内のオブジェクトを横断して一意化

const groups = [
  [{ id: 1, n: "A" }, { id: 2, n: "B" }],
  [{ id: 2, n: "B v2" }, { id: 3, n: "C" }]
];

const seen = new Set();
const flat = groups.flat();
const unique = flat.filter(x => !seen.has(x.id) && seen.add(x.id));
// [{id:1,n:"A"}, {id:2,n:"B"}, {id:3,n:"C"}]
JavaScript

外から来るネストデータはまず flat() で平坦化してから処理すると読みやすくなります。後勝ちにしたいなら Map でまとめ直します。


高度な同値判定(深い比較・構造キー)

JSON文字列をキーにする(簡易・限定的)

const items = [{a:1,b:2}, {b:2,a:1}, {a:1,b:3}];
const unique = [...new Map(items.map(x => [JSON.stringify(x), x])).values()];
// {a:1,b:2}(同じ構造)、{a:1,b:3}
JavaScript

ここが重要です:プロパティ順序が違うと別物になります。キー順を揃える“カノニカル化”が必要な場合は、キーをソートしてから文字列化します。

const canon = obj => JSON.stringify(Object.fromEntries(Object.entries(obj).sort(([a],[b]) => a.localeCompare(b))));
const unique2 = [...new Map(items.map(x => [canon(x), x])).values()];
JavaScript

深い比較はコストが高いため、可能なら“IDや正規化キー”を定義するほうが実務的です。


ストリーム・大規模データの一意化(メモリと性能)

逐次処理(見たものだけ記録)

function dedupeStream(iter, keyFn = x => x) {
  const seen = new Set();
  const out = [];
  for (const x of iter) {
    const k = keyFn(x);
    if (!seen.has(k)) { seen.add(k); out.push(x); }
  }
  return out;
}
JavaScript

ここが重要です:seen が大きくなるとメモリを食います。キー空間が巨大な場合は、期間別・シャーディング・Bloom filter(近似)などの戦略を検討します。まずは“必要な範囲だけ一意化する”スコープ設計が現実的です。


よくある落とし穴(重要ポイントの深掘り)

“同じ”の定義が曖昧

仕様で“何が同じか”を先に決めるのが最重要です。ID、大小無視、前後空白無視、全角半角、数値スケールなどを明文化してキー関数で反映します。

先勝ち・後勝ちの混乱

入力順で結果が変わります。ログや設定は“後勝ち”、履歴や最初の採用は“先勝ち”。プロダクトに合わせて明示的に選びましょう。

参照を壊す更新

一意化後に更新するならイミュータブルに(新インスタンスで置換)。辞書化(Object.fromEntries / Map)してから部分更新すると安全です。

深い比較の過コスト

JSONキーやディープイコールは重いです。実務では“比較キーを作る”方針に寄せると、性能・保守性ともに安定します。


まとめ

一意データの抽出は「Set/Map で重複を落とす」のが基本で、プリミティブは Set、オブジェクトは“IDやキー関数”で処理します。ここが重要です:同値の定義を先に決め、順序(先勝ち・後勝ち)を明示し、必要なら正規化で表記ゆれを吸収します。ネストは平坦化してから、広範囲は辞書化で O(1) に。深い比較は最終手段、性能と保守性を考え“比較キー設計”を優先する。これを押さえれば、初心者でも素早く正確に重複を排除し、実務で使える一意集合を作れます。

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