読み込み中...

JavaScriptで配列から重複を削除する方法10選

JavaScriptで配列の重複を削除する10の方法 JS
この記事は約24分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●JavaScriptで配列の重複を削除する方法とは

皆さん、JavaScriptで配列を扱っていて、重複した要素を削除したいと思ったことはありませんか?

配列の重複削除は、データの整理やパフォーマンスの向上に役立つ重要な操作です。

でも、どうやって重複を削除すればいいのか、初心者の方にはちょっとわかりにくいかもしれません。

そこで今回は、JavaScriptで配列から重複を削除する10の方法を、初心者からプロまで満足できるように解説していきます。

これから紹介する方法は、基本的なものから応用的なものまで様々です。

サンプルコードも豊富に用意していますので、実際のプロジェクトですぐに活用できるでしょう。

JavaScriptの配列操作に自信がない方も、この記事を読み進めていけば、重複削除のテクニックをマスターできるはずです。

それでは、早速見ていきましょう!

○配列の重複削除が必要な理由

そもそも、なぜ配列から重複を削除する必要があるのでしょうか?

重複削除には、主に次のようなメリットがあります。

第一に、データの整合性が保たれます。

重複したデータがあると、情報が矛盾していたり、正確性に欠けたりする可能性があります。

重複を削除することで、データの信頼性が高まります。

第二に、メモリの節約になります。重複したデータは無駄なメモリを消費します。

大規模なデータを扱う際には、この無駄が積み重なって深刻な問題につながることもあります。

第三に、パフォーマンスの向上が期待できます。

重複したデータを処理するのに余計な時間がかかってしまうため、アプリケーションの速度が低下する恐れがあります。

重複を削除しておけば、そのような問題を防げます。

●基本的な重複削除の方法

さて、JavaScriptで配列から重複を削除する基本的な方法について見ていきましょう。

ここでは、初心者の方でもすぐに使えるシンプルなテクニックを3つ紹介します。

○サンプルコード1:Set()を使う方法

まずは、ES6で導入されたSetオブジェクトを使った方法です。

Setは、ユニークな値のコレクションを表すオブジェクトで、重複した値を自動的に除外してくれます。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(originalArray)];
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードでは、まずoriginalArrayという配列を定義しています。

この配列には、重複した値である24が含まれています。

次に、new Set(originalArray)としてSetオブジェクトを作成し、スプレッド演算子(...)を使ってそれを新しい配列に変換しています。

これにより、重複が削除されたuniqueArrayが得られます。

実行結果を見ると、[1, 2, 3, 4, 5]のように、重複が取り除かれた配列が出力されていることがわかります。

Setを使う方法は、シンプルで直感的です。

ES6をサポートしている環境であれば、手軽に使えるでしょう。

ただし、元の配列の順序が保持されないことに注意が必要です。

○サンプルコード2:filter()を使う方法

次は、filter()メソッドを使った方法を見てみましょう。

filter()は、配列の各要素に対してテスト関数を実行し、その結果がtrueとなる要素だけを抽出した新しい配列を返します。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = originalArray.filter((value, index, self) => {
  return self.indexOf(value) === index;
});
console.log(uniqueArray); // [1, 2, 3, 4, 5]

ここでは、filter()のテスト関数の中で、現在の要素(value)の最初のインデックスが、現在のインデックス(index)と等しいかどうかを比較しています。

つまり、ある要素が配列内で最初に登場する位置と、現在の位置が同じであれば、その要素はユニークであると判断できます。

そのような要素だけが新しい配列に含まれることになります。

実行結果は、Setを使った場合と同じく[1, 2, 3, 4, 5]となります。

filter()を使えば、元の配列の順序を保ったまま重複を削除できるのが利点です。

ただ、テスト関数の中でindexOf()を使っているため、パフォーマンスはあまり良くありません。

大規模な配列を扱う場合は、別の方法を検討した方が良いかもしれません。

○サンプルコード3:indexOf()を使う方法

最後は、indexOf()メソッドを使った古典的な方法を紹介しましょう。

これは、ES5以前の環境でもよく使われていたテクニックです。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [];
for (let i = 0; i < originalArray.length; i++) {
  if (uniqueArray.indexOf(originalArray[i]) === -1) {
    uniqueArray.push(originalArray[i]);
  }
}
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードでは、forループを使ってoriginalArrayの各要素をチェックしています。

もし、その要素がuniqueArrayの中に存在しなければ(indexOf()の結果が-1であれば)、uniqueArrayにその要素を追加します。

こうすることで、uniqueArrayには重複のない要素だけが含まれるようになります。

実行結果は、前の2つの方法と同じです。

indexOf()を使う方法は、シンプルで理解しやすいのが利点ですが、パフォーマンスの問題があります。

配列のサイズが大きくなるほど、処理時間が長くなってしまいます。

●パフォーマンスを考慮した方法

前の章では、JavaScriptで配列から重複を削除する基本的な方法を紹介しました。でも、実際の開発現場では、もっと大規模なデータを扱うことも多いですよね。そんな時は、パフォーマンスを意識した方法を使う必要があります。

ここからは、効率性を重視した重複削除の方法を3つ見ていきましょう。これらの方法を使えば、大量のデータでも高速に処理できるはずです。

○サンプルコード4:一時オブジェクトを使う方法

まずは、一時的なオブジェクトを使ってユニークな値を抽出する方法です。オブジェクトのプロパティは一意であるという特性を利用します。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const tempObj = {};
for (let i = 0; i < originalArray.length; i++) {
  tempObj[originalArray[i]] = true;
}
const uniqueArray = Object.keys(tempObj).map(Number);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードの流れを説明しますね。まず、空のオブジェクトtempObjを作ります。そして、forループを使ってoriginalArrayの各要素をチェックしていきます。

各要素の値をtempObjのプロパティ名として設定し、値をtrueにします。こうすることで、重複した要素は上書きされ、ユニークな要素だけがプロパティとして残ります。

最後に、Object.keys()を使ってtempObjのプロパティ名を配列として取得し、map()で数値に変換します。これで、重複のないuniqueArrayが完成です。

実行結果は、[1, 2, 3, 4, 5]となります。一時オブジェクトを使う方法は、シンプルで高速です。特に、大規模な配列を扱う場合に効果的でしょう。

○サンプルコード5:ループ内でフィルタリングする方法

次は、ループの中で重複をチェックし、フィルタリングする方法を紹介します。この方法なら、新しい配列を作る必要がないので、メモリの使用量を抑えられます。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [];
const tempObj = {};
for (let i = 0; i < originalArray.length; i++) {
  if (!tempObj[originalArray[i]]) {
    tempObj[originalArray[i]] = true;
    uniqueArray.push(originalArray[i]);
  }
}
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードでは、forループの中で重複チェックとフィルタリングを同時に行っています。tempObjを使って、既に登場した要素かどうかを判定します。

もし、その要素がtempObjに存在しなければ(!tempObj[originalArray[i]]trueであれば)、その要素をtempObjに追加し、uniqueArrayにもpush()します。

こうすることで、重複のない要素だけがuniqueArrayに格納されていきます。実行結果は、先ほどと同じく[1, 2, 3, 4, 5]です。

ループ内でフィルタリングする方法は、余計な配列を作らないので、メモリ効率が良いのが特徴です。データ量が多い場合に適しています。

○サンプルコード6:reduceを使う方法

最後は、reduce()メソッドを使った少し変わった方法を見てみましょう。reduce()は配列を1つの値にまとめるメソッドですが、重複削除にも使えるんです。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = originalArray.reduce((acc, cur) => {
  if (!acc.includes(cur)) {
    acc.push(cur);
  }
  return acc;
}, []);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードでは、reduce()の第1引数にコールバック関数を、第2引数に初期値として空の配列[]を指定しています。

コールバック関数の中では、アキュムレータ(acc)に現在の要素(cur)が含まれているかどうかをincludes()でチェックしています。

もし、acccurが含まれていなければ(!acc.includes(cur)trueであれば)、acccurpush()します。こうすることで、重複のない要素だけがaccに蓄積されていきます。

最終的に、reduce()の戻り値としてaccが返され、それがuniqueArrayに代入されます。実行結果は、これまでと同じ[1, 2, 3, 4, 5]です。

reduce()を使う方法は、一時変数を使わずに重複削除できるのが利点です。ただし、コードの可読性は下がるので、チームで使う際は注意が必要かもしれません。

●複雑な配列の重複削除

これまでは、単純な数値の配列を例に重複削除の方法を見てきました。

でも、実際の開発では、もっと複雑なデータ構造を扱うことも多いですよね。

例えば、オブジェクトの配列だったり、ネストされた配列だったり。

そんな複雑な配列でも、重複削除は可能なのでしょうか?

答えは、もちろんYesです!

ここからは、少し応用的な重複削除の方法を4つ紹介していきます。

○サンプルコード7:オブジェクトの配列から重複を削除

まずは、オブジェクトの配列から重複を削除する方法を見てみましょう。

オブジェクトの配列では、単純に値を比較するだけでは重複を判定できません。

そこで、オブジェクトを一意に識別するためのキーを決めておく必要があります。

const originalArray = [
  { id: 1, name: "John" },
  { id: 2, name: "Jane" },
  { id: 1, name: "John" },
  { id: 3, name: "Bob" },
];
const uniqueArray = originalArray.filter(
  (obj, index, self) => index === self.findIndex((t) => t.id === obj.id)
);
console.log(uniqueArray);
/*
[
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' },
  { id: 3, name: 'Bob' }
]
*/

このコードでは、idプロパティをオブジェクトの一意なキーとして扱っています。

filter()メソッドの中で、現在のオブジェクト(obj)のidと、findIndex()で見つかった最初のオブジェクトのidを比較しています。

もし、現在のインデックス(index)とfindIndex()の結果が一致すれば、そのオブジェクトはユニークであると判断します。

こうすることで、重複したオブジェクトが取り除かれたuniqueArrayが得られます。

実行結果を見ると、idが重複している{ id: 1, name: "John" }は1つだけ残り、ユニークなオブジェクトの配列になっていることがわかります。

オブジェクトの配列から重複を削除する際は、一意なキーを決めておくのがポイントです。

この例ではidを使いましたが、場合によっては他のプロパティを使ったり、複数のプロパティを組み合わせたりすることもあるでしょう。

○サンプルコード8:ネストされた配列から重複を削除

次は、配列の中に配列が入っているようなネストされた構造の場合です。

ネストされた配列から重複を削除するには、再帰的なアプローチが有効です。

const originalArray = [
  [1, 2],
  [3, 4],
  [1, 2],
  [5, 6],
];
const uniqueArray = originalArray.filter(
  (arr, index, self) =>
    index === self.findIndex((t) => JSON.stringify(t) === JSON.stringify(arr))
);
console.log(uniqueArray);
/*
[
  [1, 2],
  [3, 4],
  [5, 6]
]
*/

このコードでは、JSON.stringify()を使って配列を文字列化し、その文字列で重複を判定しています。

filter()の中で、現在の配列(arr)と、findIndex()で見つかった最初の配列を文字列化して比較します。

もし、現在のインデックス(index)とfindIndex()の結果が一致すれば、その配列はユニークであると判断します。

実行結果を見ると、重複していた[1, 2]は1つだけ残り、ユニークな配列のみがuniqueArrayに格納されていることがわかります。

ネストされた配列の重複削除では、配列全体を比較する必要があるので、JSON.stringify()などを使って文字列化するのが一般的です。

ただし、パフォーマンスへの影響には注意が必要ですね。

○サンプルコード9:連想配列から重複を削除

続いては、連想配列(オブジェクト)から重複を削除する方法です。

連想配列の場合、キーが一意であることを利用して、重複を除外することができます。

const originalObject = {
  a: 1,
  b: 2,
  c: 3,
  d: 1,
  e: 2,
};
const uniqueObject = Object.entries(originalObject).reduce(
  (acc, [key, value]) => {
    if (!acc.some(([k, v]) => v === value)) {
      acc.push([key, value]);
    }
    return acc;
  },
  []
);
const uniqueArray = Object.fromEntries(uniqueObject);
console.log(uniqueArray); // { a: 1, b: 2, c: 3 }

このコードでは、まずObject.entries()を使って連想配列をキーと値のペアの配列に変換します。

次に、reduce()メソッドを使ってその配列を処理します。

reduce()のコールバック関数の中では、アキュムレータ(acc)に現在のキーと値のペア([key, value])が含まれているかどうかをsome()メソッドでチェックしています。

もし、accの中に同じ値(value)を持つペアがなければ、accにそのペアを追加します。

こうすることで、値が重複しているペアが取り除かれた配列がuniqueObjectに格納されます。

最後に、Object.fromEntries()を使ってuniqueObjectを連想配列に戻します。

れで、重複が削除された連想配列uniqueArrayが完成です。

実行結果を見ると、値が重複していたdeのペアが取り除かれ、ユニークな値だけが残っていることがわかります。

連想配列から重複を削除する際は、値だけでなくキーも考慮する必要があります。

この例では値の重複だけを見ていますが、キーの重複も気にする場合は、少し処理を工夫する必要があるでしょう。

○サンプルコード10:独自の比較関数を使う方法

最後は、もっと柔軟に重複を判定したい場合の方法です。

独自の比較関数を使えば、配列の要素同士を好きな条件で比較できます。

const originalArray = [
  { id: 1, name: "John", age: 25 },
  { id: 2, name: "Jane", age: 30 },
  { id: 3, name: "John", age: 25 },
  { id: 4, name: "Bob", age: 35 },
];
const uniqueArray = originalArray.filter(
  (obj, index, self) =>
    index ===
    self.findIndex((t) => t.name === obj.name && t.age === obj.age)
);
console.log(uniqueArray);
/*
[
  { id: 1, name: 'John', age: 25 },
  { id: 2, name: 'Jane', age: 30 },
  { id: 4, name: 'Bob', age: 35 }
]
*/

このコードでは、nameageの両方が等しいオブジェクトを重複とみなしています。

filter()の中で、現在のオブジェクト(obj)と、findIndex()で見つかった最初のオブジェクトのnameageを比較しています。

もし、現在のインデックス(index)とfindIndex()の結果が一致すれば、そのオブジェクトはユニークであると判断します。

こうすることで、nameageが重複しているオブジェクトが取り除かれたuniqueArrayが得られます。

実行結果を見ると、{ id: 3, name: "John", age: 25 }が重複として扱われ、取り除かれていることがわかります。

独自の比較関数を使う方法は、とても柔軟性が高いです。

複数のプロパティを組み合わせたり、完全一致ではなく部分一致で重複を判定したりと、様々なニーズに対応できます。

●よくあるエラーと対処法

これまで、JavaScriptで配列から重複を削除するための様々な方法を見てきました。

でも、実際にコードを書いていると、思わぬエラーに遭遇することがありますよね。

ここからは、重複削除を実装する際によくあるエラーと、その対処法について解説していきます。

エラーが発生した時に慌てないように、一緒に対策を考えていきましょう。

○TypeErrorが発生する原因と対処法

重複削除の処理を書いていて、突然TypeErrorが発生することがあります。

例えば、次のようなコードを実行すると、エラーが起きてしまいます。

const originalArray = [1, '2', 2, '3', 4, '4', 5];
const uniqueArray = [...new Set(originalArray)];
console.log(uniqueArray); // TypeError: object is not iterable

このエラーは、Setに渡された配列の要素の型が混在しているために発生しています。

Setは、全ての要素を厳密等価(===)で比較するため、数値の2と文字列の'2'は別物として扱われます。

このような場合の対処法は、配列の要素を全て同じ型に揃えることです。

例えば、map()メソッドを使って、全ての要素を数値に変換してからSetに渡すと、エラーが解消されます。

const originalArray = [1, '2', 2, '3', 4, '4', 5];
const uniqueArray = [...new Set(originalArray.map(Number))];
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このように、配列の要素の型には十分注意が必要です。

特に、ユーザー入力を配列に格納する場合などは、予期せぬ型の値が紛れ込むことがあるので、エラーハンドリングを丁寧に行いましょう。

○パフォーマンスが悪化する原因と対策

重複削除の処理を実装したのに、パフォーマンスが悪化してしまった経験はありませんか?

特に、大量のデータを扱う際は、効率の悪いコードが致命的な問題につながることがあります。

例えば、次のようなコードは、一見問題なさそうですが、パフォーマンス面で課題があります。

const originalArray = [/* 大量の要素 */];
const uniqueArray = originalArray.filter(
  (value, index, self) => self.indexOf(value) === index
);
console.log(uniqueArray);

このコードでは、filter()の中でindexOf()を使って重複をチェックしています。

しかし、indexOf()は配列の先頭から要素を検索するため、配列のサイズが大きくなるほど処理に時間がかかります。

パフォーマンスを改善するには、indexOf()の代わりにSetを使うのが有効です。

Setは、要素の追加や検索が高速なので、大量のデータでも効率的に重複を削除できます。

const originalArray = [/* 大量の要素 */];
const uniqueArray = [...new Set(originalArray)];
console.log(uniqueArray);

このように、使うメソッドや関数によってパフォーマンスに大きな差が出ることがあります。

特に、大規模なデータを扱う場合は、時間計測をしながらコードを最適化していくことが大切です。

○削除が意図した通りにならない場合の確認ポイント

重複削除の処理を書いたつもりなのに、期待した結果が得られないことがあります。

そんな時は、次のようなポイントを確認してみましょう。

まず、配列の要素の型が揃っているかどうかを確認します。

先ほども触れたように、型の不一致が原因でエラーが発生したり、意図しない重複が残ったりすることがあります。

次に、重複の判定条件が正しいかどうかを確認します。

特に、オブジェクトの配列から重複を削除する場合は、比較するプロパティが適切かどうかを見直す必要があります。

最後に、重複削除の処理自体が正しく書けているかどうかを確認します。

コードのミスやロジックの誤りが原因で、期待した結果が得られないことがあります。

例えば、次のようなコードは、一見重複を削除できそうですが、実際には重複が残ってしまいます。

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = originalArray.filter((value, index) => index === value);
console.log(uniqueArray); // [1, 2, 3, 4, 5, 5]

このコードでは、filter()の条件式がindex === valueとなっていますが、これでは重複を適切に判定できません。

正しくは、index === originalArray.indexOf(value)とする必要があります。

●重複削除のユースケースと発展的な話題

さて、ここまでJavaScriptで配列から重複を削除する様々な方法を見てきました。

でも、実際の開発現場では、どのようなシーンで重複削除が役立つのでしょうか?

ここからは、重複削除の実践的なユースケースと、より発展的な話題について探っていきたいと思います。

皆さんの開発スキルに磨きをかける上で、きっと参考になるはずです。

○ユニークな値を抽出する応用例

重複削除の一般的なユースケースの1つに、ユニークな値の抽出があります。

例えば、Eコマースサイトで商品のカテゴリ一覧を表示する際、重複したカテゴリ名を取り除いてユニークな一覧を作りたいことがあります。

const products = [
  { id: 1, name: 'シャツ', category: 'アパレル' },
  { id: 2, name: 'パンツ', category: 'アパレル' },
  { id: 3, name: '靴', category: 'シューズ' },
  { id: 4, name: 'スニーカー', category: 'シューズ' },
  { id: 5, name: '帽子', category: 'アクセサリー' },
];

const uniqueCategories = [...new Set(products.map(product => product.category))];
console.log(uniqueCategories); // ['アパレル', 'シューズ', 'アクセサリー']

このコードでは、商品の配列productsから、ユニークなカテゴリ名の配列uniqueCategoriesを作っています。

map()を使ってカテゴリ名だけを抽出し、Setで重複を削除しています。

こうすることで、重複のないカテゴリ一覧を簡単に作ることができます。

このテクニックは、ユーザーの入力から重複を取り除いたり、APIのレスポンスから必要なデータを抽出したりする場合にも応用できるでしょう。

○大規模なデータでの重複削除のコツ

重複削除は、大規模なデータを扱う際に特に重要になります。

例えば、数万件以上のユーザーデータから重複したメールアドレスを取り除きたい場合などです。

そんな時は、Setを使った方法が効果的です。

Setは内部的にハッシュテーブルを使っているため、大量のデータでも高速に重複を削除できます。

const userEmails = [
  'user1@example.com',
  'user2@example.com',
  'user1@example.com',
  'user3@example.com',
  'user2@example.com',
  // ...大量のデータ
];

const uniqueEmails = [...new Set(userEmails)];
console.log(uniqueEmails);

このコードでは、ユーザーのメールアドレスの配列userEmailsから、重複を取り除いたuniqueEmailsを作っています。

Setのおかげで、データ量が多くても効率的に処理できます。

ただし、Setはメモリ上に全てのユニークな値を保持するため、データ量が膨大な場合はメモリ不足になる可能性があります。

そのような場合は、データをバッチ処理するなどの工夫が必要です。

例えば、1万件ずつデータを読み込んで重複を削除し、結果を順次ファイルに書き出すといった方法が考えられます。

Node.jsのfsモジュールなどを使えば、このような処理を実装できるでしょう。

○ライブラリを使った効率的な重複削除

JavaScriptには、配列の操作に特化したライブラリがいくつもあります。

それらを使えば、重複削除をより簡潔に書くことができます。

例えば、Lodashのuniqメソッドを使えば、配列から重複を削除するのは一行で済みます。

const _ = require('lodash');

const originalArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = _.uniq(originalArray);
console.log(uniqueArray); // [1, 2, 3, 4, 5]

このコードでは、Lodashをインポートし、uniqメソッドに配列を渡すだけで重複が削除されています。

とてもシンプルで読みやすいコードになります。

他にも、Underscoreのuniqメソッドや、Ramda.jsのuniq関数など、様々なライブラリで重複削除をサポートしています。

プロジェクトで既にそれらのライブラリを使っているなら、ぜひ活用してみてください。

ただし、ライブラリを使う際は、性能への影響を考慮する必要があります。

大量のデータを扱う場合は、ライブラリの実装によってはパフォーマンスが劣化することがあります。

常に、ライブラリを使うことがベストな選択とは限りません。

状況に応じて、生のJavaScriptで書くことも検討しましょう。

まとめ

JavaScriptで配列から重複を削除する方法は、Setfilter()indexOf()などの基本的な方法から、パフォーマンスを考慮した方法、複雑なデータ構造に対応する方法まで、様々なアプローチがあります。

重複削除を実装する際は、エラーハンドリングとパフォーマンスに注意が必要です。

ユニークな値の抽出や大規模データの処理など、実践的な場面で重複削除のスキルが役立つでしょう。

状況に応じてライブラリを活用するのも選択肢の一つです。

この記事で紹介した方法を習得し、実践で活かすことで、JavaScriptプログラマーとしてのスキルアップにつながるはずです。