何をしたいユーティリティか:「配列の重複抽出」
ここでの「重複抽出」は、配列の中で「1 回しか出てこないもの」ではなく、「2 回以上出てくるもの」を取り出す処理です。
重複削除(ユニーク化)は「かぶりを消す」でしたが、
重複抽出は「どれがかぶっているかをあぶり出す」イメージです。
例えばこうです。
// 元の配列
[1, 1, 2, 3, 3, 3]
// 重複抽出の結果(どの値が重複しているか)
[1, 3]
JavaScript業務だと、
- 入力データの中で「同じ ID が複数回出てきていないか」をチェックしたい
- CSV の中で「重複コード」を検出してエラーにしたい
といった場面でよく使います。
一番シンプルな重複抽出:プリミティブ値の場合
出現回数を数えて「2 回以上」のものを取る
まずは、数値や文字列などのプリミティブ値だけの配列を対象にします。
やりたいことはシンプルで、
- 各値が何回出てきたかを数える
- 出現回数が 2 回以上の値だけを集める
です。
実装例:count → filter
function findDuplicatesPrimitive(array) {
if (!Array.isArray(array)) {
return [];
}
const counts = new Map();
for (const value of array) {
const current = counts.get(value) ?? 0;
counts.set(value, current + 1);
}
const duplicates = [];
for (const [value, count] of counts.entries()) {
if (count > 1) {
duplicates.push(value);
}
}
return duplicates;
}
JavaScript重要なポイントをかみ砕いて説明する
Map で「値ごとのカウント」を持つ
const counts = new Map();
for (const value of array) {
const current = counts.get(value) ?? 0;
counts.set(value, current + 1);
}
JavaScriptここでやっているのは、「値 → 出現回数」の表を作ることです。
counts.get(value) で今までの回数を取り出し、
なければ 0 とみなして +1 して、set し直しています。
例えば [1, 1, 2, 3, 3, 3] を通すと、最終的にこうなります。
1→ 2 回2→ 1 回3→ 3 回
「2 回以上」のものだけを抽出する
const duplicates = [];
for (const [value, count] of counts.entries()) {
if (count > 1) {
duplicates.push(value);
}
}
JavaScriptここで、「出現回数が 2 回以上」の値だけを duplicates に入れています。
結果として、「どの値が重複しているか」の一覧が取れます。
実際の動き
findDuplicatesPrimitive([1, 1, 2, 3, 3, 3]);
// [1, 3]
findDuplicatesPrimitive(["A", "B", "A", "C", "B"]);
// ["A", "B"]
findDuplicatesPrimitive([true, false, true]);
// [true]
findDuplicatesPrimitive([]);
// []
JavaScript「重複している要素そのもの」を取り出したい場合
値ではなく「配列の要素」を返す版
さっきの関数は「重複している値の一覧」でしたが、
「重複している行(要素)を全部取りたい」こともあります。
例えば、
[1, 1, 2, 3, 3, 3]
JavaScriptから、
[1, 1, 3, 3, 3]
JavaScriptのように、「重複しているものだけを抜き出す」イメージです。
function extractDuplicateElementsPrimitive(array) {
if (!Array.isArray(array)) {
return [];
}
const counts = new Map();
for (const value of array) {
const current = counts.get(value) ?? 0;
counts.set(value, current + 1);
}
const result = [];
for (const value of array) {
if ((counts.get(value) ?? 0) > 1) {
result.push(value);
}
}
return result;
}
JavaScript実際の動き
extractDuplicateElementsPrimitive([1, 1, 2, 3, 3, 3]);
// [1, 1, 3, 3, 3]
extractDuplicateElementsPrimitive(["A", "B", "A", "C", "B"]);
// ["A", "A", "B", "B"]
JavaScript「どの値が重複しているか」だけ知りたいなら findDuplicatesPrimitive、
「重複している行を全部取りたい」なら extractDuplicateElementsPrimitive、
という使い分けができます。
オブジェクト配列の重複抽出:キーを指定する
「id が重複しているレコード」を見つけたい
業務では、こんな配列がよく出てきます。
const users = [
{ id: 1, name: "Alice" },
{ id: 1, name: "Alice (duplicate)" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Carol" },
{ id: 3, name: "Carol (duplicate)" },
];
JavaScriptここで、「id が重複しているレコードだけを抽出したい」という要件はかなり頻出です。
uniqueByKey の「重複版」:findDuplicatesByKey
function findDuplicatesByKey(array, key) {
if (!Array.isArray(array)) {
return [];
}
const counts = new Map();
for (const item of array) {
if (item == null || typeof item !== "object") {
continue;
}
const value = item[key];
const current = counts.get(value) ?? 0;
counts.set(value, current + 1);
}
const result = [];
for (const item of array) {
if (item == null || typeof item !== "object") {
continue;
}
const value = item[key];
if ((counts.get(value) ?? 0) > 1) {
result.push(item);
}
}
return result;
}
JavaScript重要なポイント
1 回目のループで「キーの値ごとの出現回数」を数え、
2 回目のループで「2 回以上出てくるキーを持つ要素だけ」を結果に入れています。
実際の動き
const users = [
{ id: 1, name: "Alice" },
{ id: 1, name: "Alice (duplicate)" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Carol" },
{ id: 3, name: "Carol (duplicate)" },
];
findDuplicatesByKey(users, "id");
// [
// { id: 1, name: "Alice" },
// { id: 1, name: "Alice (duplicate)" },
// { id: 3, name: "Carol" },
// { id: 3, name: "Carol (duplicate)" },
// ]
JavaScriptこれで、「重複している id を持つレコードだけ」を一気に取り出せます。
任意の「キー関数」で重複抽出する
複数項目の組み合わせで重複を決めたい場合
例えば、「code と version の組み合わせが重複している行を抽出したい」場合、
単純な key 文字列では足りません。
そのときは、「要素から重複判定用のキーを作る関数」を渡せるようにします。
function findDuplicatesBy(array, keyFn) {
if (!Array.isArray(array)) {
return [];
}
const counts = new Map();
for (const item of array) {
const key = keyFn(item);
const current = counts.get(key) ?? 0;
counts.set(key, current + 1);
}
const result = [];
for (const item of array) {
const key = keyFn(item);
if ((counts.get(key) ?? 0) > 1) {
result.push(item);
}
}
return result;
}
JavaScript実際の使い方
const items = [
{ code: "A", version: 1 },
{ code: "A", version: 1 },
{ code: "A", version: 2 },
{ code: "B", version: 1 },
{ code: "B", version: 1 },
];
const duplicates = findDuplicatesBy(
items,
(item) => `${item.code}:${item.version}`
);
// 結果
// [
// { code: "A", version: 1 },
// { code: "A", version: 1 },
// { code: "B", version: 1 },
// { code: "B", version: 1 },
// ]
JavaScriptkeyFn の中で、「重複判定に使いたい情報」を文字列などにまとめて返してあげれば OK です。
実務で意識してほしい設計のポイント
「重複削除」と「重複抽出」をセットで考える
重複削除(ユニーク化)と重複抽出は、よくセットで使います。
- 重複削除 → 正常系の処理(ユニークな一覧を作る)
- 重複抽出 → 異常系の検知(入力におかしな重複がないかチェックする)
例えば、CSV で「商品コード一覧」を受け取るとき、
findDuplicatesByKeyで「重複コード」がないかチェックする- 問題なければ
uniqueByKeyでユニークな一覧を作る
という流れにすると、「入力チェック」と「正常処理」がきれいに分かれます。
「何をもって重複とみなすか」を明文化する
重複抽出は、「同じとは何か?」をはっきりさせる作業です。
- プリミティブ配列なら「値が同じなら重複」。
- オブジェクト配列なら「id が同じなら重複」。
- 場合によっては「code+version の組み合わせが同じなら重複」。
これをユーティリティの関数名や引数(key や keyFn)で表現しておくと、
あとからコードを読んだ人にも意図が伝わりやすくなります。
エラー表示やログと組み合わせる
重複抽出は、「そのまま使う」だけでなく、「エラーの材料」としても使えます。
const duplicates = findDuplicatesByKey(users, "id");
if (duplicates.length > 0) {
const ids = Array.from(new Set(duplicates.map((u) => u.id)));
console.error("重複しているIDがあります:", ids);
// ここでバリデーションエラーとして返す、など
}
JavaScriptこうしておけば、「どの ID が重複しているか」をユーザーに返したり、
ログに残したりするのも簡単です。
少し手を動かして感覚をつかむ
コンソールで、次のようなコードを実際に打ってみてください。
findDuplicatesPrimitive([1, 1, 2, 3, 3, 3]);
extractDuplicateElementsPrimitive([1, 1, 2, 3, 3, 3]);
const users = [
{ id: 1, name: "Alice" },
{ id: 1, name: "Alice (duplicate)" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Carol" },
{ id: 3, name: "Carol (duplicate)" },
];
findDuplicatesByKey(users, "id");
const items = [
{ code: "A", version: 1 },
{ code: "A", version: 1 },
{ code: "A", version: 2 },
{ code: "B", version: 1 },
{ code: "B", version: 1 },
];
findDuplicatesBy(items, (item) => `${item.code}:${item.version}`);
JavaScript「どの値・どの要素が“重複しているもの”として抽出されているか」を、
自分の目で確認してみてください。
そのうえで、自分のプロジェクトに
export function findDuplicatesPrimitive(...) { ... }
export function extractDuplicateElementsPrimitive(...) { ... }
export function findDuplicatesByKey(...) { ... }
export function findDuplicatesBy(...) { ... }
JavaScriptのような関数を置き、
「配列の重複をチェックしたくなったら、必ずこの“重複抽出ユーティリティ”を通す」
というルールを作ってみてください。
それだけで、あなたの配列バリデーションは、場当たり的な for 文から、
意図と一貫性を備えた「業務レベルの検証ロジック」に変わっていきます。
