【JavaScript】「いいね」「お気に入り」機能の実装に最適!Setオブジェクトでデータを管理する方法
JavaScriptで複数のデータを管理する際、最もよく使われるのは「配列(Array)」ですが、もう一つの便利な選択肢として Set(セット) オブジェクトがあります。
Setの最大の特徴は「同じ値(重複)を許可しない」ことです。 この特性を利用して「配列から重複データを削除する」という使い方が有名ですが、実はデータの追加や削除、存在チェック(含まれているかどうか)を行うための便利なメソッドが最初から用意されており、非常に使い勝手が良いのです。
今回は、配列との違いやSetオブジェクトの基本的なメソッドの使い方を、「お気に入り機能」のデモと一緒に解説します!
デモ動作
See the Pen JSのSetオブジェクト入門 by mikanbako (@mikanbako) on CodePen.
demo: Setオブジェクト入門
実装コード紹介
【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]
補足:公式ドキュメント
まとめ
今回はJSの Set オブジェクトについて解説しました。
配列から重複を削除するテクニックが有名ですが、デモのように「ユーザーが選択した項目(IDなど)を管理する」といった場面では、重複を自動で防いでくれて、追加や削除、存在チェックが直感的に行える Set が非常に適しています。
データ構造の特性を理解して「配列」と「Set」を適切に使い分けられるようになると、一段階レベルアップした美しいコードが書けるようになるので、ぜひ活用してみてください!