読み込み中...

TypeScriptのSetを完全に理解するたったの10のステップ

TypeScript Setの完全ガイドのカバーイメージ TypeScript
この記事は約23分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Web開発においてデータ構造は非常に重要です。

その中で、最近のJavaScriptやTypeScriptの世界で注目を浴びているのが、Setオブジェクトです。

この記事では、TypeScriptにおけるSetの基本的な性質について徹底的に解説していきます。

10のステップを経ることで、Setの機能を完全に理解することができるようになります。

具体的なサンプルコードとともに学ぶことで、初心者でもプロでも、TypeScriptのSetを最大限に活用できるスキルを身につけることができます。

●TypeScriptのSetとは

TypeScriptのSetは、ES6で導入されたJavaScriptのSetを基にしています。

Setは、重複しない要素の集合を表すデータ構造で、重複する要素を追加しようとしても無視されるという特性を持っています。

また、Setは要素の追加、読み出し、削除などの基本的な操作が可能です。

Setは、配列やオブジェクトとは異なる振る舞いやメソッドを持っているため、正しく使いこなすには独自の知識が必要です。

例えば、配列のようにインデックスで要素にアクセスすることはできません。

○Setの基本的な性質

□重複しない要素のみ保持

Setの最大の特性は、重複する要素を持たないことです。

もし重複する要素を追加しようとしても、それは自動的に無視されます。

これにより、ユニークな要素の集合を簡単に作成・管理することができます。

このコードではSetに数字を追加しています。

しかし、重複する2は追加されていません。

   let numbers = new Set();
   numbers.add(1);
   numbers.add(2);
   numbers.add(2); // この行は無視される
   numbers.add(3);

このコードを実行すると、Setには1, 2, 3の3つの要素のみが格納されます。

□要素の順序

Setは要素の追加された順序を保持します。

しかし、Setはインデックスを持たず、要素へのアクセスは順序を持たない方法で行われるため、配列のような操作はできません。

□要素のアクセス

Setにはインデックスがないため、特定の要素に直接アクセスすることはできません。

要素へのアクセスは、Setのメソッドを使用して行う必要があります。

●TypeScriptのSetの使い方

TypeScriptは、JavaScriptに静的型機能を追加するための言語です。

JavaScriptの多くの組み込みオブジェクトやメソッドは、TypeScriptでも利用できますが、より型安全に利用することができるのが特徴です。

この記事では、TypeScriptでのSetの使い方に焦点を当てて、その詳細を徹底的に解説します。

Setは、JavaScriptとTypeScriptの両方で使用できる組み込みのオブジェクトです。

主な特徴として、各要素がユニークであること、つまり重複する要素が存在しないことが挙げられます。

この性質により、Setは頻繁に要素のユニーク性を保持する必要がある場面、例えば重複データの除外や、ユニークなIDのリストの管理などに適しています。

○サンプルコード1:Setの基本的な作成と利用

では、実際にTypeScriptでSetを使ってみましょう。

基本的なSetの作成とその利用方法に関するサンプルコードを紹介します。

// Setの作成
const numbers: Set<number> = new Set();

// 要素の追加
numbers.add(1);
numbers.add(2);
numbers.add(3);

// Setの内容を表示
console.log([...numbers]);

このコードでは、まずSet<number>という型で新しいSetを作成しています。

このSetは数値のみを要素として持つことができます。

その後、addメソッドを使用して3つの数値をSetに追加しています。

最後に、スプレッド構文を利用してSetの内容を配列として出力しています。

このコードを実行すると、コンソール上には[1, 2, 3]という配列が表示されるでしょう。

○サンプルコード2:Setへの要素の追加と削除

TypeScriptのSetにおける基本的な操作のひとつに、要素の追加と削除があります。

この操作は日常的なプログラミングタスクで頻繁に行われるため、正確に理解しておくことが必要です。

まず、Setへの要素の追加は、addメソッドを使用して行います。

このメソッドは、引数として追加する要素を受け取り、要素が正常に追加された後のSet自体を返します。

これにより、メソッドチェーンを使用して複数の要素を連続して追加することも可能です。

下記のコードは、新しくSetを作成し、数値の要素を追加しています。

// 新しいSetを作成
const numbersSet: Set<number> = new Set();

// 要素の追加
numbersSet.add(1).add(2).add(3);
console.log(numbersSet); // 出力結果:Set { 1, 2, 3 }

このコードでは、新しく作成されたnumbersSetに、1, 2, 3の3つの要素を追加しています。

このコードを実行すると、結果としてSet { 1, 2, 3 }がコンソールに表示されるでしょう。

一方、Setから要素を削除する場合は、deleteメソッドを使用します。

このメソッドは、引数として削除する要素を受け取り、要素が正常に削除された場合はtrueを、そもそもその要素が存在しなかった場合はfalseを返します。

下記のコードでは、先ほどのnumbersSetから、2という要素を削除しています。

// 要素の削除
const result = numbersSet.delete(2);
console.log(result);         // 出力結果:true
console.log(numbersSet);     // 出力結果:Set { 1, 3 }

上記のコードを実行すると、まずtrueがコンソールに表示され、その後にSet { 1, 3 }が表示されるでしょう。

これは、2という要素が正常に削除され、その結果としてSetから2が消えたことを示しています。

○サンプルコード3:Setの要素の検索と存在確認

TypeScriptのSetを使用する際、その中に特定の要素が存在するかどうかを確認する方法は非常にシンプルです。

具体的にはhasメソッドを使用します。このメソッドは指定した要素がSetの中に存在する場合、trueを返し、存在しない場合はfalseを返します。

この機能を詳細に解説したサンプルコードを紹介します。

// Setの作成
const fruits = new Set<string>();
fruits.add("apple");
fruits.add("banana");
fruits.add("cherry");

// "apple"がfruitsの中に存在するか確認
const hasApple = fruits.has("apple"); // このコードはappleがfruitsに含まれているかを確認しています。

// "grape"がfruitsの中に存在するか確認
const hasGrape = fruits.has("grape"); // このコードはgrapeがfruitsに含まれているかを確認しています。

このコードではまず、fruitsという名前のSetを作成しています。

その後、3つの果物(”apple”, “banana”, “cherry”)をそのSetに追加しています。

そして、hasメソッドを利用して、”apple”や”grape”といった要素がSetに存在するかどうかを確認しています。

このコードを実行すると、hasAppleにはtrueが代入され、hasGrapeにはfalseが代入されます。

これは、”apple”はfruitsの中に存在するのに対して、”grape”は存在しないためです。

このように、Setの中に特定の要素が存在するかどうかを瞬時に確認することが可能です。

特に大量のデータを扱う際など、配列を用いて線形的に要素を探索するよりも、Setを使用した方が効率的である場面も多いです。

また、TypeScriptでは型の制約を持つため、上記のコード例では<string>という型引数を指定して、文字列のみを要素とするSetを作成しています。

これにより、意図しないデータ型の要素が追加されることを防ぐことができます。

●Setの便利なメソッドたち

TypeScriptにおけるSetは、多数の便利なメソッドを備えています。

これらのメソッドを使うことで、Setの要素の操作や情報の取得が効率的に行えます。

今回は、特に重要と思われるメソッドについて、サンプルコードとともに詳しく解説していきます。

○サンプルコード4:forEachメソッドでの要素の取り出し

// Setの初期化
const sampleSet = new Set(["apple", "banana", "cherry"]);

// forEachメソッドを使用して、Setの全要素を取り出し
sampleSet.forEach((fruit) => {
    console.log(fruit);
});

このコードでは、まず新しいSetを初期化して、3つのフルーツの文字列を要素として持つsampleSetを作成しています。

次に、forEachメソッドを利用して、Setの各要素を順番に取り出し、コンソールに出力しています。

このコードを実行すると、次の出力が得られます。

apple
banana
cherry

TypeScriptのSetのforEachメソッドは、配列のそれと非常に似ています。

ただし、Setの特性上、重複した要素が存在しないため、確実にユニークな値のみが取り出される点が利点と言えます。

○サンプルコード5:sizeプロパティで要素数の取得

TypeScriptのSetクラスでは、sizeプロパティを使って、そのSetに含まれる要素の数を簡単に取得することができます。

これは、例えば、要素の数に応じて何かの処理を実行したい場合や、要素の数がある特定の数以上、あるいは以下であることを確認したい場合などに非常に役立ちます。

では、実際にsizeプロパティを使ったサンプルコードを見てみましょう。

// Setの作成
let mySet: Set<number> = new Set([1, 2, 3, 4, 5]);

// sizeプロパティで要素数の取得
let setSize: number = mySet.size;

console.log(`Setに含まれる要素の数: ${setSize}`);

このコードでは、まず初めに数字の要素を持ったSetを作成しています。

その後、sizeプロパティを使ってSetに含まれる要素の数を取得し、変数setSizeに格納しています。

最後に、console.logを使って、取得した要素の数をコンソールに表示しています。

このコードを実行すると、Setには5つの要素が含まれているので、「Setに含まれる要素の数: 5」というメッセージがコンソールに表示されます。

○サンプルコード6:clearメソッドでの全要素の削除

TypeScriptを学ぶ過程で、データの集合を管理したいと考えることが増えてきます。そんな時に役立つのが、Setです。

前のステップでは、Setの基本的な使い方やその他の便利なメソッドを見てきました。

ここでは、Setから全ての要素を一括で削除するclearメソッドについて学びます。

TypeScriptのSetにおけるclearメソッドは、その名の通り、Set内の全ての要素をクリアする役割があります。

もし、一時的に集合を作成し、後でそのデータをすべて消去したい場面や、再利用する前に初期状態に戻したい場合などに、このメソッドは非常に役立ちます。

それでは、具体的なサンプルコードを見てみましょう。

// Setの初期化
let sampleSet: Set<number> = new Set([1, 2, 3, 4, 5]);

// Set内の要素を表示
console.log(sampleSet);  // 出力結果: Set { 1, 2, 3, 4, 5 }

// clearメソッドを使用して全要素を削除
sampleSet.clear();

// 再度Set内の要素を表示
console.log(sampleSet);  // 出力結果: Set {}

このコードでは、まず数字の配列を元にSetを初期化しています。

その後、clearメソッドを使用することで、Set内の全ての要素を一度に削除しています。

このコードを実行すると、最初は1, 2, 3, 4, 5という5つの要素を持ったSetが表示されます。

しかし、clearメソッドを実行した後は、要素が存在しない空のSetが表示されることが確認できます。

●Setを活用した応用例

TypeScriptのSetは基本操作だけでなく、さまざまな応用的なシーンでの活用が可能です。

その中で、特によく使用されるテクニックとして「配列とSetの相互変換」があります。

この技術を使うことで、配列のデータをSetに変換したり、Setのデータを配列に変換したりすることができ、それによって配列の操作やデータ管理をより柔軟に行うことが可能となります。

○サンプルコード7:配列とSetの相互変換

配列をSetに変換する際のメリットとして、配列内の重複要素を自動的に削除することが挙げられます。

これはSetの特性上、同じ値を持つ要素が1つしか保存されないためです。

下記のコードでは、まず重複した要素を持つ配列を用意し、それをSetに変換しています。

// 重複要素を持つ配列を用意
const numbersArray: number[] = [1, 2, 3, 4, 5, 2, 3, 6];

// 配列をSetに変換
const numbersSet: Set<number> = new Set(numbersArray);

// Setを出力
console.log(numbersSet); 

このコードでは、numbersArrayという重複要素を持つ配列を用意しています。

その後、その配列をnew Set()の引数に渡すことで、配列をSetに変換しています。

この操作を行うと、numbersSetには重複した「2」や「3」といった要素は1つしか含まれていないことが確認できます。

逆に、Setを配列に変換する際は、スプレッド構文を利用する方法が一般的です。

下記のコードは、先程のnumbersSetを配列に戻す例を示しています。

// Setを配列に変換
const convertedArray: number[] = [...numbersSet];

// 変換した配列を出力
console.log(convertedArray); 

このコードを実行すると、convertedArray[1, 2, 3, 4, 5, 6]という重複のない要素から成る配列として出力されます。

続いて、これらのサンプルコードを実行した結果について解説します。

先程のnumbersSetの出力結果は、Set { 1, 2, 3, 4, 5, 6 }となります。

これは、重複する「2」と「3」が削除されていることが確認できます。

一方、convertedArrayの出力結果は[1, 2, 3, 4, 5, 6]となります。

これにより、Setを配列に正しく変換できていることが確認できます。

○サンプルコード8:Setを利用したデータのフィルタリング

TypeScriptにおいて、Setは一意の値を持つコレクションを表すオブジェクトです。

このコレクションの特性を活用することで、データのフィルタリングや重複の除去などの操作が効率的に行えます。

今回は、Setを使ったデータのフィルタリング方法について解説します。

まず、基本的なサンプルコードから始めます。

// 数字の配列を定義
const numbers = [1, 2, 2, 3, 4, 4, 5];

// 配列をSetに変換して重複を削除
const uniqueNumbers = new Set(numbers);

// Setを配列に変換
const filteredNumbers = [...uniqueNumbers];

console.log(filteredNumbers); // [1, 2, 3, 4, 5]

このコードでは、まず数字の配列numbersを定義しています。

次に、この配列をnew Set(numbers)でSetオブジェクトに変換することで、自動的に重複する要素が削除されます。

最後に、スプレッド構文[...uniqueNumbers]を使用して、Setを再び配列に変換しています。

このコードを実行すると、重複した数字が除去された配列[1, 2, 3, 4, 5]が出力されます。

しかし、単純な重複の除去だけでなく、Setを活用してさらに複雑なフィルタリングを行うことも可能です。

例として、特定の条件を満たす要素だけを取り出す方法を見てみましょう。

// 数字の配列を定義
const numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100];

// 50以上の数字だけを取り出すフィルタリング関数
const filterFunction = (num: number) => num >= 50;

// フィルタリング関数を使用してSetを作成
const filteredSet = new Set(numbers.filter(filterFunction));

console.log([...filteredSet]); // [50, 60, 70, 80, 90, 100]

このコードでは、まず数字の配列numbersを定義しています。

続いて、50以上の数字だけを取り出すフィルタリング関数filterFunctionを定義しています。

この関数をnumbers.filter(filterFunction)で配列に適用し、その結果をSetに変換することで、条件を満たす要素だけのSetを作成しています。

このコードを実行すると、50以上の数字だけが含まれる配列[50, 60, 70, 80, 90, 100]が出力されます。

○サンプルコード9:Setの合併、交差、差分

TypeScriptのSetは、ユニークな値を持つコレクションとして利用されます。

ここでは、2つ以上のSetを操作する際の合併、交差、差分の方法について、サンプルコードとともに詳しく解説します。

□合併

Setの合併とは、2つのSetに含まれる要素をすべて集めた新しいSetを作成する操作を指します。

このコードでは、new Set()を使って新しいSetを作成し、...(スプレッド構文)を用いて元のSetの要素を展開しています。

const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);

const union = new Set([...setA, ...setB]);
console.log(union); // Set { 1, 2, 3, 4, 5 }

このコードを実行すると、setAsetBの要素をすべて含んだSet { 1, 2, 3, 4, 5 }が作成されます。

□交差

交差とは、2つのSetに共通する要素だけを取り出して新しいSetを作成する操作を指します。

このコードでは、Array.from(setA)でSetを配列に変換し、filter()メソッドを用いてsetBに含まれる要素だけを取り出しています。

const intersection = new Set(Array.from(setA).filter(item => setB.has(item)));
console.log(intersection); // Set { 3 }

このコードを実行すると、setAsetBの共通する要素3だけが含まれるSet { 3 }が作成されます。

□差分

差分とは、あるSetの要素のうち、もう一つのSetに含まれない要素だけを取り出して新しいSetを作成する操作を指します。

このコードでは、同じくArray.from(setA)でSetを配列に変換し、filter()メソッドでsetBに含まれない要素だけを取り出しています。

const difference = new Set(Array.from(setA).filter(item => !setB.has(item)));
console.log(difference); // Set { 1, 2 }

このコードを実行すると、setAの要素のうちsetBに含まれない12だけが含まれるSet { 1, 2 }が作成されます。

●TypeScriptでのSetの型安全性

TypeScriptは、JavaScriptのスーパーセットであり、強力な型システムを持つ言語です。

この型システムを利用することで、コードの品質や安全性を高めることができます。

特に、データの集合を扱う際のSetに関しても、TypeScriptはその型安全性を発揮します。

JavaScriptにおけるSetは、値の集合を表すためのデータ構造であり、その中の要素はユニーク(重複しない)でなければなりません。

しかし、JavaScriptのSetは型の概念を持っていません。

これに対して、TypeScriptのSetは、型パラメータを使用して、格納される要素の型を指定することができます。

この型パラメータを使うことで、Setに格納する要素の型を明示的に指定することができ、コンパイル時に型の不整合を発見することが可能になります。

○サンプルコード10:型を持ったSetの利用

では、具体的にサンプルコードを通じて、TypeScriptのSetの型安全性を詳しく見ていきましょう。

// 数字のみを持つSetを定義
let numberSet: Set<number> = new Set();

// 数値を追加
numberSet.add(1);
numberSet.add(2);
numberSet.add(3);

// 文字列を追加しようとするとコンパイルエラーが発生
// numberSet.add("four"); // Error: Type 'string' is not assignable to type 'number'.

// Setの中身を出力
for (let num of numberSet) {
    console.log(num);
}

このコードでは、Set<number>という型パラメータを使用して、数字のみを要素として持つSetを定義しています。

したがって、数値以外のデータ型、たとえば文字列をnumberSetに追加しようとすると、TypeScriptのコンパイラはエラーを出力します。

このコードを実行すると、console.logの部分でSetに格納された数値が順番に出力されます。

つまり、次のような結果が得られます。

1
2
3

この型の指定によって、意図しないデータの追加や操作を防ぐことができ、バグの発生を大幅に減少させることが期待できます。

特に大規模なプロジェクトや複数人での開発時には、このような型の安全性は非常に有効です。

『TypeScriptのSetを完全に理解するたったの10のステップ』

●注意点と対処法

TypeScriptのSetは非常に強力なデータ構造の一つです。

しかし、その機能性を最大限に引き出すためには、その特性や振る舞い、そして注意点を正しく理解する必要があります。

ここでは、TypeScriptのSetを使用する上での主な注意点と、それらの問題を解消するための対処法について詳しく解説します。

○Setのユニーク性を理解する

まず、Setの最も大きな特徴として「ユニーク性」があります。

これは、Set内の各要素が一意であるということを意味します。

例えば、同じ値を持つ要素をSetに何度追加しても、それは一つの要素としてしか保存されません。

このコードでは、Setに同じ値を持つ要素を追加しています。

let sampleSet = new Set();
sampleSet.add(1);
sampleSet.add(2);
sampleSet.add(2);
sampleSet.add(3);
console.log(sampleSet);

このコードを実行すると、Setは「1, 2, 3」という三つの要素のみを持っていることがわかります。

2を2回追加しても、一つの要素としてしか認識されません。

しかし、ユニーク性には注意が必要です。

特に、オブジェクトなどの参照型のデータをSetに追加する場合、異なるオブジェクトであっても、内容が同じであればそれらは別々の要素として保存されます。

○等価性の挙動とその対処

次に、TypeScriptのSetでは「等価性」の判定が重要となります。

基本的に、Setでは===での厳密な等価性の比較が行われます。

これは、プリミティブ型のデータに対しては問題ないのですが、参照型のデータ、特にオブジェクトに対しては注意が必要です。

例えば、次のようなコードを考えてみましょう。

let obj1 = { name: 'Taro' };
let obj2 = { name: 'Taro' };

let sampleSet = new Set();
sampleSet.add(obj1);
sampleSet.add(obj2);
console.log(sampleSet.size);

このコードでは、obj1obj2は内容的には同じですが、異なるオブジェクトとして認識されます。

そのため、Setのサイズは2と表示されます。

このような参照の違いによる等価性の問題を解消するためには、オブジェクトの内容を直接比較する方法や、オブジェクトを一度文字列に変換してからSetに追加する方法などが考えられます。

このコードでは、オブジェクトをJSON文字列に変換してからSetに追加しています。

let obj1 = { name: 'Taro' };
let obj2 = { name: 'Taro' };

let sampleSet = new Set();
sampleSet.add(JSON.stringify(obj1));
sampleSet.add(JSON.stringify(obj2));
console.log(sampleSet.size);

この方法を使うことで、オブジェクトの内容が同じ場合、同一の要素としてSetに追加されることが確認できます。

●カスタマイズ方法

TypeScriptでSetをカスタマイズする方法は数多く存在します。

これには、TypeScriptの継承機能を利用したSetの拡張や、Setに特有の機能を追加することなどが含まれます。

ここでは、TypeScriptのSetをカスタマイズする主要な方法と、それに関連するサンプルコードを紹介します。

○Setの継承と拡張

TypeScriptでは、クラスの継承を用いて既存のSetを拡張することができます。

これにより、Setに新しいメソッドやプロパティを追加して、より使いやすくしたり、特定の機能を持つSetを作成することが可能となります。

例として、Setに要素を追加する際に、追加される要素の数をログとして出力する機能を持つ拡張されたSetを考えてみましょう。

// 拡張されたSetクラスの定義
class LoggingSet<T> extends Set<T> {
  // 要素を追加するメソッドをオーバーライド
  add(value: T): this {
    console.log(`要素${value}を追加します。`);
    super.add(value);
    return this;
  }
}

// 拡張されたSetの使用例
const logSet = new LoggingSet<number>();
logSet.add(1);
logSet.add(2);

このコードでは、LoggingSetという新しいクラスを定義しています。

このクラスは標準のSetクラスを継承しており、addメソッドをオーバーライドして新しい機能を追加しています。

要素を追加する際に、その要素が何であるかをコンソールにログ出力します。

上記のコードを実行すると、次のような出力が得られます。

要素1を追加します。
要素2を追加します。

まとめ

TypeScriptのSetは、プログラム内で重複しない要素のコレクションを簡単に管理するための非常に有用なデータ構造です。

今回の記事では、TypeScriptのSetの基本的な性質から使い方、便利なメソッド、応用例、型安全性に関する内容、そして注意点やカスタマイズ方法について徹底的に解説しました。

今回の内容を経て、TypeScriptのSetに関する知識が深まったことを願っています。

実際に手を動かしながら、サンプルコードを試してみることで、より理解を深めることができるでしょう。

引き続き、TypeScriptの他の機能やデータ構造についても学んでいくことで、より高度なプログラミングスキルを身につけることができることを期待しています。