iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

集合論に学ぶJavaScriptのSet

はじめに

こんにちはiimonでフロントエンドエンジニアをしている「みよちゃん」です!

普段業務でTypeScriptを使用していますが、皆さんもご存じのSetに関して、重複を削除する以外の用途で使用したことがなく、いまいちピンときていませんでした。

最近個人的に数学を勉強する機会があり、その中の一つに「群論」がありました。これは、代数学の基礎になる分野で「集合」や「関係」を含みます。この集合・関係について学ぶなかで、Setについてハッと思う部分があったので、まとめたいと思います!

※この記事はお堅いタイトルですが、自分が数学を勉強する中でふと思ったことをベースに書いているため、数学的理論に基づいているわけではありません。

Setとは

Setの語源

個人的に物事の概念を理解するにあたり、語源を知ることは重要だと思います。特にCSの分野では英語に由来する命名が多いため、これを理解するとしないとでは概念の大枠を捉えることができるかに雲泥の差があるのではないかと思います。

自分の個人的な考えはさておき、Setは「集合」という意味を含みます。

Setは集合である

そう、Setとは集合なのです!!

皆さん高校の数Aで集合について勉強されたと思うので、何となくイメージがつくのではないかと思います。

こんなやつです!

集合とは特定の条件を満たす要素の集まりを指します。上記の例では

U⇒10未満の自然数

A⇒2の倍数

B⇒3の倍数

というような集合になります。

このようにJavaScriptにおけるSetとはユニーク要素の集まりを管理するデータ構造です。

Setの用途

代表的なメソッド

Setオブジェクトの代表的なメソッドをさらっと紹介したいと思います。

add

集合に対して同じ値を持つ要素がない場合、指定した値を持つ新しい要素を追加する。

const setA = new Set();

setA.add(1);
setA.add(2);
setA.add(2);
for (const item of setA){
    console.log(item);
}
//output => 1, 2

clear

集合からすべての要素を取り除く

const setA = new Set();

setA.add(1);
setA.add(2);
setA.add(3);

setA.clear();
console.log(setA.size);
// output => 0

delete

指定された値が集合内にあれば、取り除く

const setA = new Set();

setA.add(1);
setA.add(2);
setA.add(3);

setA.delete(3);
console.log(setA.size);
// output => 2

has

集合内に指定された要素があるかどうかを返す

const setA = new Set();

setA.add(1);
setA.add(2);
setA.add(3);

console.log(setA.has(3));
// output => true

集合演算をSetを使用して実装する

前述したとおり、JavaScriptのSetオブジェクトは集合演算をするためのインターフェースです。ここで先ほど例に挙げた集合を用いて実際にSetを用いて簡単な集合演算を実装したいと思います。 集合U,A,BをSetで定義すると以下のようになります

const setU = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9]);
const setA = new Set([2, 4, 6, 8]);
const setB = new Set([3, 6, 9]);

和集合

A・Bのどちらか一方、もしくは両方の集合に属する要素を取得する

const setA = new Set([2, 4, 6, 8]);
const setB = new Set([3, 6, 9]);
const union = new Set([...setA, ...SetB]);
union.forEach(part => console.log(part))
// output => 2, 4, 6, 8, 3, 9

積集合

A・Bどちらの集合にも属する要素を取得する

const setA = new Set([2, 4, 6, 8]);
const setB = new Set([3, 6, 9]);

const intersection = new Set([...setA].filter(x => setB.has(x)));
intersection.forEach(x => console.log(x));
// output => 6

差集合

A集合に属しておりかつB集合には属していない要素を取得する

const setA = new Set([2, 4, 6, 8]);
const setB = new Set([3, 6, 9]);

const difference = new Set([...setA].filter(x => !setB.has(x)));
difference.forEach((x => console.log(x));
// output => 2, 4, 8

対象差

A集合・B集合どちらか一方にのみ属す要素を取得する

const setA = new Set([2, 4, 6, 8]);
const setB = new Set([3, 6, 9]);

const symmetricDifference = new Set([...setA].filter(x => !setB.has(x)).concat(
                                    [...setB].filter(x => !setA.has(x))));
symmetricDifference.forEach(x => console.log(x));
// output => 2, 4, 8, 3, 9

おわりに

今回は自分が数学を勉強している時に、ふと思ったことを深掘りした結果以前からピンと来ていなかったSetの用途が何となく理解できたので記事にまとめてみました。

Setのhasメソッドは、配列のincludesよりも存在の有無を判別することに最適化されており無駄がないとされていますが、今回の集合演算では一度配列に展開してからfilterメソッドを使用して演算をしていることも加味すると、その恩恵を最大限に活用できているかと考えると何とも言えないな〜と思いました。

結論として「Setは集合というユニークな要素の集まりを扱うためのオブジェクト」というまとめになりますが、自身がこれまで持っていた「重複を削除するためのもの」というイメージも遠からずではありましたが、より正しいイメージが持てたのかなと思います。

最後まで記事を読んでいただきありがとうございます! 弊社では現在エンジニアを募集しております!

この記事を読んで少しでも弊社に興味を持っていただけましたら是非カジュアル面談でお話ししましょう!!お話しできるのを楽しみにしています!! Wantedly / Green

参考記事

developer.mozilla.org