【JavaScript】「いいね」「お気に入り」機能の実装に最適!Setオブジェクトでデータを管理する方法

JavaScriptで複数のデータを管理する際、最もよく使われるのは「配列(Array)」ですが、もう一つの便利な選択肢として Set(セット) オブジェクトがあります。

Setの最大の特徴は「同じ値(重複)を許可しない」ことです。 この特性を利用して「配列から重複データを削除する」という使い方が有名ですが、実はデータの追加や削除、存在チェック(含まれているかどうか)を行うための便利なメソッドが最初から用意されており、非常に使い勝手が良いのです。

今回は、配列との違いやSetオブジェクトの基本的なメソッドの使い方を、「お気に入り機能」のデモと一緒に解説します!

デモ動作

See the Pen JSのSetオブジェクト入門 by mikanbako (@mikanbako) on CodePen.

全画面でのデモ動作は以下のリンクからご確認下さい!(別タブで開かれます)

実装コード紹介

【HTML】

<div class="l-wrap">
  <h2>Setオブジェクト 活用デモ</h2>
  <p class="p-desc">「いいね」した商品のIDをSetオブジェクトで管理しています。</p>
  
  <div class="p-demo-box">
    
    <!-- 商品リスト -->
    <div class="c-item-list">
      <button class="c-btn-like js-btn-like" data-id="item_001">🍎 りんごをいいね!</button>
      <button class="c-btn-like js-btn-like" data-id="item_002">🍌 ばななをいいね!</button>
      <button class="c-btn-like js-btn-like" data-id="item_003">🍇 ぶどうをいいね!</button>
    </div>

    <!-- 結果表示エリア -->
    <div class="p-output">
      <p>お気に入り登録数: <span class="js-like-count">0</span> 件</p>
      <p>登録済みIDリスト: <span class="js-like-list">なし</span></p>
    </div>

  </div>
</div>

【CSS】

/* ベース装飾 */
.l-wrap {
  padding: 40px 20px;
  max-width: 600px;
  margin: 0 auto;
  text-align: center;
  color: #3e3e3e;
  line-height: 1.6;
}

h2 {
  margin-bottom: 10px;
  font-size: 16px;
  color: #1bb4d4;
}

.p-desc {
  margin-bottom: 30px;
  font-size: 16px;
  font-weight: bold;
  color: #666;
}

.p-demo-box {
  padding: 40px;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 15px rgb(0 0 0 / 5%);
}

/* ボタンの装飾 */
.c-item-list {
  display: flex;
  flex-direction: column;
  gap: 15px;
  margin-bottom: 30px;
}

.c-btn-like {
  padding: 12px;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  background: #fff;
  border: 2px solid #ccc;
  border-radius: 8px;
  transition: all 0.3s;
}

/* いいね!された時のスタイル */
.c-btn-like.is-active {
  color: #e83e8c;
  background: #ffe4e1;
  border-color: #e83e8c;
}

/* 出力エリア */
.p-output {
  padding: 20px;
  font-size: 16px;
  font-weight: bold;
  text-align: left;
  background: #e0f7fa;
  border-radius: 8px;
}

.p-output span {
  color: #e83e8c;
}

【JavaScript】

// 💡 1. Setオブジェクトの作成(いいねしたIDを保存する入れ物)
const likedItems = new Set();

// HTML要素の取得
const buttons = document.querySelectorAll('.js-btn-like');
const countDisplay = document.querySelector('.js-like-count');
const listDisplay = document.querySelector('.js-like-list');

buttons.forEach((btn) => {
  btn.addEventListener('click', () => {
    // クリックされたボタンの固有IDを取得
    const itemId = btn.dataset.id;

    // 💡 2. .has() でSetの中にすでにIDが存在するかチェック!
    if (likedItems.has(itemId)) {
      // 存在する場合は「いいね解除」なので、Setから削除(.delete())
      likedItems.delete(itemId);
      btn.classList.remove('is-active');
    } else {
      // 存在しない場合は「いいね登録」なので、Setに追加(.add())
      // ※Setは重複を許さないので、何度追加しても1つにしかなりません
      likedItems.add(itemId);
      btn.classList.add('is-active');
    }

    // 画面の表示を更新する
    updateDisplay();
  });
});

// 表示を更新する関数
function updateDisplay() {
  // 💡 3. .size でSetの中身の数を取得(配列のlengthの代わり)
  countDisplay.innerText = likedItems.size;

  if (likedItems.size === 0) {
    listDisplay.innerText = "なし";
  } else {
    // Setは配列ではないので、Array.from() や [...Set] で配列に変換してから表示する
    listDisplay.innerText = Array.from(likedItems).join(', ');
  }
}

コード解説

今回のデモでは、ボタンをクリックした際に以下の流れで処理を行っています。

1. 登録済みかどうかのチェックと切り替え(.has, .delete, .add)

// 💡 2. .has() でSetの中にすでにIDが存在するかチェック!
if (likedItems.has(itemId)) {
  // 存在する場合は「いいね解除」なので、Setから削除(.delete())
  likedItems.delete(itemId);
  btn.classList.remove('is-active');
} else {
  // 存在しない場合は「いいね登録」なので、Setに追加(.add())
  // ※Setは重複を許さないので、何度追加しても1つにしかなりません
  likedItems.add(itemId);
  btn.classList.add('is-active');
}

likedItems.has(itemId) を使って、クリックされた商品のIDがすでにSetの中に存在するかを判定しています。 すでに存在していれば「いいね解除」なので .delete() で削除し、存在していなければ「いいね登録」なので .add() で追加します。このように if文 と組み合わせてトグル(切り替え)処理を作るのが鉄板パターンです!

2. 登録数の取得(.size)

// 💡 3. .size でSetの中身の数を取得(配列のlengthの代わり)
countDisplay.innerText = likedItems.size;

現在いくつ「いいね」されているかの件数を表示するために、.size を使っています。配列の .length と全く同じ感覚で使えます!

3. 画面へのリスト表示(Array.from())

// Setは配列ではないので、Array.from() や [...Set] で配列に変換してから表示する
listDisplay.innerText = Array.from(likedItems).join(', ');

Setオブジェクトはそのままでは配列の便利なメソッド(joinなど)が使えません。そのため、画面に「item_001, item_002」のように文字列として繋げて出力する際は、Array.from(likedItems) を使って一度通常の配列に変換してから処理を行っています。

これだけ覚えればOK!Setの基本メソッド

  • .add(値):データを追加します。(すでに同じ値がある場合は無視されます)
  • .delete(値):指定したデータを削除します。
  • .has(値):指定したデータが存在するかを true / false で返します。
  • .clear():中身をすべて空にします。
  • .size:データの数を取得します。(配列の .length に相当します)

配列から重複を省く超定番テクニック

すでに配列としてデータを持っていて、そこから重複を削除したい場合は、スプレッド構文(...)と組み合わせて1行で書くことができます!

const numbers = [1, 2, 2, 3, 3, 3, 4];
// Setにして重複を消した後、すぐ配列に戻す!
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4]

補足:公式ドキュメント

・MDN: Set

まとめ

今回はJSの Set オブジェクトについて解説しました。

配列から重複を削除するテクニックが有名ですが、デモのように「ユーザーが選択した項目(IDなど)を管理する」といった場面では、重複を自動で防いでくれて、追加や削除、存在チェックが直感的に行える Set が非常に適しています。

データ構造の特性を理解して「配列」と「Set」を適切に使い分けられるようになると、一段階レベルアップした美しいコードが書けるようになるので、ぜひ活用してみてください!

Prev