旧ブログ(ISSEN)から移行しました

【JavaScript】データ重複を許さないSetクラスの紹介

【JavaScript】データ重複を許さないSetクラスの紹介

杉田侑祐
杉田侑祐6分で読めます

はじめに

この記事の概要

こんにちは、株式会社TOKOSの杉田です!
今回はJavaScriptのSetクラスの基本的な使い方から、実践的な応用例まで詳しく解説していきます!
あまり使用する頻度はないかもしれませんが重複を許さないユニークな値の集合を扱う場合などに活躍します!
ぜひ最後までご覧ください!

この記事では、以下の内容について詳しく解説します:

  • Setクラスの使用方法

対象読者

  • 初学者の方
  • フロントエンド開発をされている方

この記事で扱う内容、扱わない内容

この記事で扱う内容 :

  • Setクラスの概要

この記事で扱わない内容:

  • JavaScriptの基本的な使用方法

概要

JavaScriptのSetクラスは、ES6(ECMAScript 2015)で導入されました!
冒頭でもお伝えしましたが、重複を許さないユニークな値の集合を扱う場合などで使用します!

Setクラスの基礎

Setオブジェクトは、プリミティブ値やオブジェクト参照を含む、どのような型のユニーク(一意)の集合も保持できます。
Setの主な特徴は下記になります。

  • 値が一度だけ格納される(重複不可)
  • 順序が保持される(挿入順)
  • キーを使わず値のみを格納
  • NaNとundefinedも格納可能

また、紛らわしいですが配列とは別物になります。
配列との違いは下記になります。

重複要素の扱い

  • Set
    • 重複する値を保持できない
  • 配列
    • 同じ値を複数持つことができる

要素の検索効率

  • Set
    • has()メソッドによる要素検索は非常に高速
  • 配列
    • includes()indexOf()による検索はSetに劣る

要素へのアクセス

  • Set
    • インデックスによる直接アクセスはできない
  • 配列
    • array[index]のように直接インデックスでアクセスできる

Setクラスの主要メソッド

Setクラスの主要メソッドは下記になります。

Setのメソッド配列での同等の操作説明
new Set()[]新しい空のコレクションを作成
add(value)push(value)要素を追加する
delete(value)splice(array.indexOf(value), 1)特定の値を削除する
has(value)includes(value) または indexOf(value) !== -1値が存在するか確認する
clear()length = 0 または splice(0)すべての要素を削除する
forEach(callback)forEach(callback)各要素に対してコールバックを実行
values()values()値のイテレータを返す
entries()entries()キーと値のペアのイテレータを返す(配列では[index, value])

基本的な使用例

要素の追加・削除などメソッドを使用した例は下記になります。

JavaScript
// 新しいSetを作成
const colors = new Set()
 
// 要素を追加
colors.add("赤")
colors.add("青")
colors.add("緑")
 
console.log(colors.size) // 3
 
// 重複要素の追加は無視される
colors.add("赤")
console.log(colors.size) // 3(変わらない)
 
// 要素の存在確認
console.log(colors.has("赤")) // true
console.log(colors.has("黄")) // false
 
// 要素の削除
colors.delete("青")
console.log(colors.size) // 2
 
// すべての要素を削除
colors.clear()
console.log(colors.size) // 0

上記例のように、重複要素が追加される場合は無視されます!

配列との相互変換

冒頭で配列とは別物とお伝えしましたが、相互変換ができます!

JavaScript
// 配列からSetへの変換
const array = [1, 2, 2, 3, 4, 4, 5]
const uniqueSet = new Set(array)
console.log(uniqueSet) // Set(5) {1, 2, 3, 4, 5}
 
// SetからArrayへの変換
const uniqueArray = [...uniqueSet]
console.log(uniqueArray) // [1, 2, 3, 4, 5]
 
// または Array.from() を使う方法
const anotherUniqueArray = Array.from(uniqueSet)
console.log(anotherUniqueArray) // [1, 2, 3, 4, 5]

実践的な応用例

ここからよく使うパターンを紹介します!

配列から重複を削除する

JavaScript
function removeDuplicates(array) {
  return [...new Set(array)]
}
 
const numbersWithDuplicates = [1, 2, 2, 3, 4, 4, 5, 5, 5]
const uniqueNumbers = removeDuplicates(numbersWithDuplicates)
console.log(uniqueNumbers) // [1, 2, 3, 4, 5]

2つの配列の共通要素を見つける(交差)

JavaScript
function intersection(arrayA, arrayB) {
  const setB = new Set(arrayB)
  return arrayA.filter((element) => setB.has(element))
}
 
const array1 = [1, 2, 3, 4, 5]
const array2 = [3, 4, 5, 6, 7]
console.log(intersection(array1, array2)) // [3, 4, 5]

2つの配列の差分を見つける(差集合)

JavaScript
function difference(arrayA, arrayB) {
  const setB = new Set(arrayB)
  return arrayA.filter((element) => !setB.has(element))
}
 
const array1 = [1, 2, 3, 4, 5]
const array2 = [3, 4, 5, 6, 7]
console.log(difference(array1, array2)) // [1, 2]

2つの配列を結合して重複を削除(和集合)

JavaScript
function union(arrayA, arrayB) {
  return [...new Set([...arrayA, ...arrayB])]
}
 
const array1 = [1, 2, 3]
const array2 = [2, 3, 4, 5]
console.log(union(array1, array2)) // [1, 2, 3, 4, 5]

オブジェクト配列から特定のプロパティの重複を削除

JavaScript
function getUniqueByProperty(array, prop) {
  const seen = new Set()
  return array.filter((item) => {
    const value = item[prop]
    if (seen.has(value)) {
      return false
    }
    seen.add(value)
    return true
  })
}
 
const users = [
  { id: 1, name: "田中" },
  { id: 2, name: "鈴木" },
  { id: 3, name: "田中" }, // 重複した名前
  { id: 4, name: "佐藤" },
]
 
const uniqueUsers = getUniqueByProperty(users, "name")
console.log(uniqueUsers)
// [{ id: 1, name: '田中' }, { id: 2, name: '鈴木' }, { id: 4, name: '佐藤' }]

パフォーマンスの考慮点

大量のデータを扱う場合、Setは配列操作よりもパフォーマンスが良いです。
下記コードは配列とSetのパフォーマンスを比較した例です。
Setのhas()は配列のincludes()と比べて、実行時間が100分の1程度になります。

JavaScript
// パフォーマンス比較の例
const largeArray = Array.from({ length: 100000 }, (_, i) => i)
const largeSet = new Set(largeArray)
 
console.time("Array includes")
const inArray = largeArray.includes(99999)
console.timeEnd("Array includes") // Array includes: ~5-10ms
 
console.time("Set has")
const inSet = largeSet.has(99999)
console.timeEnd("Set has") // Set has: ~0.01-0.05ms

さいごに

今回はあまり馴染み深くないSetクラスについて紹介しました!
配列を使用することが多いとは思いますが、扱うデータによっては実装が簡潔になり、パフォーマンスも向上するため、ぜひ活用してみてください!

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️