【JavaScript】配列の重複削除とは?初心者にわかりやすく複数のコード例で解説
はじめに
JavaScriptで配列を取り扱うとき、同じ値が複数入ってしまうことがあります。
このような重複要素は、データ処理を行ううえで思わぬミスにつながるかもしれません。
たとえばユーザーが入力フォームでデータを複数回送信したり、異なるAPIから取得した結果を合体したりするシーンを想像してみてください。
同じデータが重複していると、後々の分析や画面への表示で煩わしくなることが少なくないでしょう。
そのため、配列から重複した要素を取り除く方法を理解することは、開発の初期段階からとても重要です。
本記事では、初心者の方にもできるだけわかりやすく説明できるよう、複数の方法を取り上げます。
この記事を読むとわかること
- JavaScript配列の重複削除 がなぜ必要になるのか
- Set を使った重複削除の方法
- filter メソッドなどを使ったほかのやり方
- 実務ではどのようなシーンで活用できるのか
- データの重複を防ぐために気をつけたいポイント
ここから、具体的なコード例や実務の活用シーンを交えながら順を追って解説します。
JavaScript配列で重複削除が必要になる理由
まずは、なぜ配列から重複要素を削除しなくてはいけないのか、という点を考えます。
大まかには以下のような理由があります。
1文ずつ見ていきましょう。
データの精度を高める
アプリケーションで扱うデータに重複があると、統計や集計結果に影響を与えることが多いです。
たとえば売上データを管理する場合に、同じ金額のエントリが何度も加算されてしまうと、実際より大きな数字を算出してしまう可能性があります。
このようなエラーを防ぐためにも、重複削除は欠かせません。
処理の効率化
不要なデータが増えれば増えるほど、配列をループするときに余計な処理が発生します。
もし重複する要素が多数含まれていると、検索や計算が無駄に長い時間かかってしまい、アプリの動作が遅くなるかもしれません。
不要な重複を削除し、配列をすっきりさせておくと、コードのパフォーマンスを保ちやすくなるでしょう。
UIの分かりやすさ
重複データがあれば、画面に同じ情報が何度も表示されることになり、ユーザーから見ると混乱を招くかもしれません。
たとえばユーザー名一覧を表示するだけなのに、同じユーザー名がいくつも並んでいると、どれが本物なのかわからなくなります。
そうした混乱を防ぎ、UIを見やすくするためにも重複削除は大事です。
配列から重複を削除することで、データの正確性と処理の効率、そしてユーザー体験のわかりやすさが向上しやすいです。
配列から重複を削除する基本的な方法:Setを活用する
JavaScriptで重複削除といえば、まず Set を使ったやり方が最もシンプルでしょう。
基本的な構文を見てみます。
// 重複した数値の配列 const numbers = [1, 2, 2, 3, 4, 4, 5]; // Setを使って重複を排除 const uniqueNumbers = [...new Set(numbers)]; console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
Setとは何か
Set はJavaScriptの組み込みオブジェクトの一種であり、その名の通り集合を表すのに近い動きをします。
要素を追加するときに、すでに存在する値が含まれていた場合は重複を許可しない、という性質があります。
つまり、ある配列から new Set (配列) を作ると、自動的に重複が取り除かれたかたちで値が保持されます。
その後、このままでは Set のインスタンスなので、そのまま配列のように使うことができません。
そこで配列に戻すために [...]
のようなスプレッド構文を使って、配列 に展開するわけです。
実務での活用シーン
たとえば、ユーザーが選択するタグやカテゴリーなどをまとめて管理するときに、同じタグを二重に登録したくない場面があります。
そういったときに Set を使えば、かんたんにユニークなリストを得ることができます。
また、複数のAPIやデータソースから情報を取得して配列を結合する場合にも重宝するでしょう。
この方法はコードも短く、わかりやすいため、入門としては最適です。
filterメソッドを用いた重複削除
次にご紹介するのは、filter を使ったやり方です。
Setを使うほど短くはありませんが、条件を指定した上で重複を排除したいときには有効な方法になります。
const fruits = ["apple", "orange", "orange", "banana", "apple"]; // filterを使った重複削除 const uniqueFruits = fruits.filter((item, index, array) => { return array.indexOf(item) === index; }); console.log(uniqueFruits); // ["apple", "orange", "banana"]
仕組みを解説
array.indexOf(item)
は、配列の先頭からアイテムを検索して、最初に見つかった位置を返すメソッドです。filter
のコールバック関数の第三引数array
には、メソッド呼び出し元の配列、つまりfruits
が入っています。- そのため、
array.indexOf(item)
は “アイテムが最初に出現した位置” を返します。 - もしこれが、現在のイテレーションのインデックス
index
と同じであれば、それは初回出現であるという判断ができます。 - 逆に言うと、2回目以降に見つかった要素は “初回の位置” と “現在の位置” が異なるので、filterによって除外されます。
この方法のメリット
- 条件付きでフィルタリングしながら重複を排除したい場合にも応用できます。
- たとえば数値が一定以上の場合は重複を取り除く、といった書き方もフィルター関数の中で柔軟に書き込めるわけです。
実務での活用シーン
フロントエンドのアプリケーションで、検索結果をまとめて一覧表示するときに重複を排除したいが、ついでに何らかの条件で絞り込みも行いたいときに便利です。
filter
をうまく活用すれば、一度のメソッド呼び出しで “重複削除” と “条件による除外” を同時に実行できます。
reduceメソッドを用いた重複削除
もう少し応用的な方法として、reduce を使うパターンがあります。
これは初心者の方にとっては少しハードルが高いかもしれませんが、配列を一つひとつ処理しながら結果を構築していくため、カスタマイズ性に優れたやり方です。
const animals = ["cat", "dog", "dog", "bird", "cat"]; // reduceを使った重複削除 const uniqueAnimals = animals.reduce((accumulator, current) => { if (!accumulator.includes(current)) { accumulator.push(current); } return accumulator; }, []); console.log(uniqueAnimals); // ["cat", "dog", "bird"]
reduceの動き
reduce
メソッドは、配列を左から右へ順に処理し、累積値(ここではaccumulator
)を返します。- 初期値として空の配列
[]
を設定しておき、配列の要素を順に見ながら含まれていないものだけを追加していきます。 - 最終的に返される
accumulator
が重複削除済みの配列となります。
カスタマイズの可能性
accumulator
がオブジェクトだったり、複数の配列をまとめたものだったり、さまざまな形に応用できます。
たとえば、オブジェクト配列のなかで特定のキーの重複を取り除きたいときも、この仕組みを少し書き換えるだけで対応できます。
実務で、「データをいったんオブジェクトマッピングしながらユニークなデータにまとめたい」というケースがあれば、reduce を組み合わせる方法が役立つでしょう。
実務でありがちな応用例:オブジェクト配列の重複削除
配列がプリミティブな値ばかり(文字列や数値ばかり)のケースなら、前述のやり方がそのまま使えます。
ただし、オブジェクト配列の場合はすこし工夫が必要です。
Set や indexOf
ではオブジェクト比較をうまく扱えないケースが多いためです。
IDやキーを基準に重複を排除
多くの場合、オブジェクトの内容すべてが重複かどうかで判定するのではなく、「主キー」となるIDなどを基準にチェックする方法が一般的ではないでしょうか。
たとえば次のような配列があったときに、 id
が重複するデータを取り除きたいとします。
const users = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 2, name: "Robert" }, { id: 3, name: "Charlie" }, { id: 1, name: "Alicia" } ];
ここで id
が1のデータが重複し、2も重複しています。
実際には、同じユーザーであれば id: 1
と id: 1
は同一とみなせるため、後方にあるレコードを削除してしまいたい、などの要望があるかもしれません。
reduceでIDの重複をチェックする例
const uniqueUsers = users.reduce((acc, current) => { // すでに同じidを持つオブジェクトがaccに含まれているか確認 const exists = acc.some(user => user.id === current.id); if (!exists) { acc.push(current); } return acc; }, []); console.log(uniqueUsers); // [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 3, name: "Charlie" } ]
この例では、先に登場したデータを優先する かたちで重複を排除しています。
もし後のデータを優先したい場合は、上書きするなどのロジックを挟めばよいでしょう。
同じIDなのに、まったくの別ユーザーを指す可能性があるかもしれません。
実際のシステムでは、どういう基準でデータをユニークと見なすのか、業務要件やDB設計にあわせて慎重に考えることが重要です。
filterとMapを組み合わせる例
別の方法として、一時的に Map などを利用してユニーク判定をおこないつつ、最後にfilterで絞り込む方法も考えられます。
ただし、実装がやや複雑になりますので、要件が明確な場合には reduce だけでも十分対応可能です。
重複データを防ぐための設計上の工夫
ここまで、配列に含まれる重複を削除する方法をいろいろ見てきました。
しかし、そもそも重複が入りにくい設計にしておけば、後から削除する手間を減らせるかもしれません。
入力時のバリデーション
フォームや入力画面で、同じ情報が登録されそうになったら警告を出すというやり方があります。
UIで防ぎきれない場合もありますが、少なくともフロントエンド側のバリデーションを設定しておくと、同じデータが増えるリスクを下げられます。
データベースのユニーク制約
もしサーバーサイドやデータベースを扱える状況であれば、ユニーク制約を設定するのも有効でしょう。
重複が起きそうになったときにエラーを返す設計にしておけば、そもそも同じIDや同じ属性のデータが蓄積されることを防げます。
APIのレスポンス時点で除外する
複数のAPIを利用してデータを集約するケースでは、レスポンスを返す時点で重複をまとめて除外してしまう方法もあります。
これでフロントエンド側が余計な負担を負わなくて済むことも多いでしょう。
配列の重複削除で注意したいポイント
実務では、単純に重複を削除しておしまい、というわけにはいかない場合もあります。
いくつか気をつけておきたい点を見てみます。
データ形式の違いによる重複
文字列と数値が混在するケースを考えてみてください。
たとえば ["1", 1]
のように、文字列としての "1" と数値の 1 は別物です。
単純に重複とみなすかどうかは、状況によって異なるかもしれません。
必要に応じて型変換や整合性チェックを行い、意図しない区別が発生しないように配慮しましょう。
パフォーマンス上の影響
小さな配列なら問題ありませんが、非常に大きな配列で何度も重複削除を行うと、処理時間が増えてしまう可能性があります。
filter
+ indexOf
などは内部で都度検索を行うため、要素数が多くなると負荷が高くなりやすいです。
同じ処理を繰り返し実行しないよう、可能な限りまとめて行うなどの工夫が必要になります。
ソートの有無
重複を削除した結果、元の順序が変化するかどうかも気になる場合があるでしょう。
Set
で重複を削除した場合は、あくまでも 挿入順 を保持する仕様ですが、思わぬ場所で順番が変わることもあります。
順序を厳密に維持したいなら、一度順序のソートを行うか、あるいは他の方法を選ぶなど検討が必要です。
実務の場面における具体的な活用例
商品一覧の統合管理
ECサイトで、複数の仕入先や複数店舗の在庫情報をひとつにまとめる場面をイメージします。
仕入先Aと仕入先Bで、同じ商品コードを持つデータが重複していると、在庫数や価格などを正しく管理できない恐れがあります。
この場合、Set や reduce を活用して、商品コードが重複する要素をまとめたり、最新情報のみ残すなどの処理をします。
ログデータの分析
サーバーから大量のログデータが届き、同じIPアドレスや同じユーザーエージェントをまとめたい場合があります。
重複をそのまま残しておくと、ログが膨大な量になり、検索しにくくなるかもしれません。
先にまとめて重複を除外しておけば、後々の分析が簡単になるでしょう。
フィードの結合
SNSのような機能を作る場合に、フォローしているユーザーの投稿や、いくつもの外部APIから取得したフィードを合体して表示するケースがあります。
同じ投稿が重複していたり、同じユーザーの情報が何回も登場したりすると、見た目が煩雑になってしまうかもしれません。
そこで一覧表示の直前に重複削除をすることで、ユーザーが混乱するのを防げるわけです。
コードのメンテナンス性を向上させるための工夫
重複削除を行うメソッドは、シンプルに書けばそれほど長くはなりません。
しかし、大規模なコードのなかで同じような操作を何度も行うのであれば、メンテナンス性を高める工夫をしておくのもひとつの方法です。
ユーティリティ関数としてまとめる
“重複削除” という操作を何度か行う場合は、専用の関数を定義して使い回す と便利です。
たとえば、以下のようにしておけば、呼び出す側が詳細な実装を知らなくても簡単にユニークな配列を取得できます。
/** * 配列の重複要素を削除して返す * @param {Array} array * @returns {Array} */ function removeDuplicates(array) { return [...new Set(array)]; } // 利用例 const result = removeDuplicates([1, 2, 2, 3, 3]); console.log(result); // [1, 2, 3]
内部実装を変えたくなったときは、この関数のなかだけ修正すれば済むので、コードの管理が楽になります。
チームでのコーディング規約に組み込む
大勢で同時に開発を進める場合、誰かが重複削除をするロジックを独自に書いていたり、ほかの誰かは違うやり方で書いていたりすると、混乱が生まれる可能性があります。
コード規約として「重複削除を行うときは removeDuplicates
関数を使おう」のように決めておけば、全員が共通のメソッドを使い、一貫性を保ちやすくなるでしょう。
テストを通じてデータの想定外ケースにも備える
初心者のうちは、配列が常に期待通りの形式だと考えて実装しがちです。
しかし実務では、いろいろなデータが飛んでくる可能性があります。
空の配列
何らかの理由で空の配列が届くことは意外と珍しくありません。
たとえば検索結果がなかったり、APIが空のレスポンスを返すケースです。
ちゃんと空の状態を処理できるようにしないと、エラーになったり、予期しない動きになるかもしれません。
意図せず null
や undefined
が混入
配列のなかに null
や undefined
といった値が混じるとき、重複削除のロジックが誤作動を起こす場合があります。
こういった値をどう扱うのか事前に決めておき、テストでも検証しておくことが望ましいでしょう。
型の混在
数値と文字列が同居している場合や、オブジェクト配列とプリミティブ配列が混ざっている場合など、想定外のケースが発生するかもしれません。
実務のテストでは、そうした境界ケースを洗い出し、コードが正しく動作するかチェックしておくと安心できます。
まとめ
JavaScriptで配列の重複を削除する方法について、いくつかのアプローチをご紹介してきました。
どの方法を選ぶかは、配列のサイズや要件、扱うデータの形によって異なります。
- まずは Set を使う方法がシンプル
filter
+indexOf
は条件つきのフィルタリングも一緒にできる- reduce はカスタマイズ性が高い
- オブジェクト配列の重複削除では、IDなどのキーを基準にすることが多い
- 必要に応じて、設計段階から重複を生まないしくみづくりを考える
- テストで意図しないデータが混ざったときの動きを検証しておく
いずれの方法を使う場合でも、実務の背景やデータの性質を理解したうえで実装することが大切です。
重複削除は、一見すると単純そうに見えて、じつはデータの取り扱い方針が明確になっていないとトラブルの原因にもなります。
配列操作に慣れていない初心者の方は、まずは Set による重複削除から試してみるのがよいでしょう。
そして要件が増えてくるにつれて filter
や reduce
を検討すると、より柔軟に対応できるはずです。
今後、JavaScriptを使ったプロジェクトで配列操作に触れる機会が多くなっていくにつれ、今回の内容が皆さんの助けになるかもしれません。
実際のコードに取り入れながら、いろいろな場面で活用してみてください。