JavaScript Set
は ES2015 規格で導入されて以来、機能が不十分と見なされてきました。しかし、今、その状況は変わろうとしています。
Sets は値が一意で重複しないことを保証する集合型です。ES2015 バージョンでは、Set
が提供する機能は主に要素の作成、追加、削除、および要素が特定の Set
に属しているかどうかの確認に限られています。複数の集合を操作または比較する必要がある場合は、自分で関数を作成する必要があります。幸いなことに、ECMAScript 規格の策定委員会 TC39 と主要なブラウザ開発者はこの点で進展を遂げています。現在、JavaScript では union
(和集合)、intersection
(共通集合)、および difference
(差集合)などの操作が利用できるようになりました。
これらの新機能を深く理解する前に、まず既存の JavaScript Sets が何をできるかを振り返り、その後に以下の 新 Set 関数 と これらの機能をサポートする JavaScript エンジン について探ってみましょう。
ES2015 バージョンの JavaScript Sets でできる操作は?#
いくつかの例を通じて JavaScript Set
の基本機能を探るのが最も直接的な方法です。
空の Set
を作成することも、イテラブルオブジェクト(配列など)を提供して Set
を初期化することもできます。
const languages = new Set(["JavaScript", "TypeScript", "HTML", "JavaScript"]);
Set
の値は一意でなければならないため、上記の Set
には実際には三つの要素が含まれています。これを確認するには、Set
の size
プロパティを使用します。
languages.size;
// => 3
add
メソッドを使用して新しい要素を Set
に追加できます。追加しようとした要素がすでに存在する場合、何も変化はありません。
languages.add("JavaScript");
languages.add("CSS");
languages.size;
// => 4
delete
メソッドを使用して Set
から要素を削除できます。
languages.delete("TypeScript");
languages.size;
// => 3
has
メソッドを使用して、特定の要素が Set
に属しているかどうかを確認できます。配列と比較して、Set
のこの確認は効率が高く、操作の時間計算量は定数時間(O(1))です。
languages.has("JavaScript");
// => true
languages.has("TypeScript");
// => false
forEach
または for...of
ループを使用して Set
の要素を反復処理することもできます。要素の順序は、Set
に追加された順序に従います。
languages.forEach(element => console.log(element));
// "JavaScript"
// "HTML"
// "CSS"
さらに、keys
、values
(実際には keys
と同等)および entries
メソッドを使用して Set
からイテレータを取得できます。
最後に、clear
メソッドを使用して Set
を空にすることができます。
languages.clear();
languages.size;
// => 0
これは、ES2015 規格の Set
で実行可能な操作の簡単な振り返りです:
Set
は一意の値の集合を扱うためのメソッドを提供します。Set
に要素を追加したり、要素がSet
に存在するかどうかをテストするのは非常に効率的です。Array
または他のイテラブルオブジェクトをSet
に変換することは、重複を排除する簡単な方法です。
しかし、この実装は Set
同士の操作能力が欠けています。二つの Set
を結合して両方のすべての要素を含む新しい Set
(和集合)を作成したり、二つの Set
の共通要素(共通集合)を見つけたり、一方の Set
にあり他方の Set
にない要素(差集合)を特定したりすることを望むかもしれません。最近まで、これらの操作を実現するにはカスタム関数が必要でした。
新しい Set 関数には何が含まれていますか?#
Set
メソッドの提案は、Set
インスタンスに以下のメソッドを追加します:union
(和集合)、intersection
(共通集合)、difference
(差集合)、symmetricDifference
(対称差集合)、isSubsetOf
(部分集合の判断)、isSupersetOf
(超集合の判断)、および isDisjointFrom
(相互に交差しないかの判断)。
これらのメソッドのいくつかは SQL の特定の結合操作に似ており、各関数の動作をコード例を通じて示します。
以下のコード例は、Chrome 122+ または Safari 17+ で試すことができます。
Set.prototype.union(other)#
二つの集合の和集合は、二つの集合のすべての要素を含む集合です。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const allLanguages = frontEndLanguages.union(backEndLanguages);
// => Set {"JavaScript", "HTML", "CSS", "Python", "Java"}
この例では、最初の集合と二番目の集合のすべての言語が三番目の集合に含まれています。他の方法で Set
に要素を追加するのと同様に、重複する要素は自動的に排除されます。
これは二つのテーブルに対して SQL の FULL OUTER JOIN
を実行するのと同等です。
Set.prototype.intersection(other)#
二つの集合の共通集合は、二つの集合に共通する要素を含む集合です。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const frontAndBackEnd = frontEndLanguages.intersection(backEndLanguages);
// => Set {"JavaScript"}
ここで、「JavaScript」は二つの集合に同時に出現する唯一の要素です。
共通集合は SQL の INNER JOIN
に相当します。
Set.prototype.difference(other)#
操作する集合と別の集合との間の差集合は、最初の集合に特有のすべての要素を含みます。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = frontEndLanguages.difference(backEndLanguages);
// => Set {"HTML", "CSS"}
const onlyBackEnd = backEndLanguages.difference(frontEndLanguages);
// => Set {"Python", "Java"}
二つの集合間の差異を特定する際には、差集合関数を呼び出す集合と引数として渡す集合の順序が非常に重要です。上記の例では、フロントエンド言語の集合からバックエンド言語の集合を除外した結果、「JavaScript」が削除され、「HTML」と「CSS」が残ります。逆に、バックエンド言語の集合からフロントエンド言語の集合を除外しても「JavaScript」が削除され、「Python」と「Java」が残ります。
差集合は SQL の LEFT JOIN
を実行するのに似ています。
Set.prototype.symmetricDifference(other)#
二つの集合の対称差集合は、二つの集合に特有のすべての要素を含む集合です。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = frontEndLanguages.symmetricDifference(backEndLanguages);
// => Set {"HTML", "CSS", "Python", "Java"}
const onlyBackEnd = backEndLanguages.symmetricDifference(frontEndLanguages);
// => Set {"Python", "Java", "HTML", "CSS"}
この場合、結果集合の要素は同じですが、要素の順序は呼び出された集合によって異なります。要素の追加順序は、それらが集合に追加された順序によって決まり、関数を操作する集合の要素が最初に追加されます。
対称差集合は SQL で二つのテーブルの共通要素を除外する FULL OUTER JOIN
に似ています。
Set.prototype.isSubsetOf(other)#
最初の集合のすべての要素が二番目の集合に存在する場合、その集合は別の集合の部分集合です。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const declarativeLanguages = new Set(["HTML", "CSS"]);
declarativeLanguages.isSubsetOf(frontEndLanguages);
// => true
frontEndLanguages.isSubsetOf(declarativeLanguages);
// => false
任意の集合は自分自身の部分集合です。
frontEndLanguages.isSubsetOf(frontEndLanguages);
// => true
Set.prototype.isSupersetOf(other)#
最初の集合が二番目の集合のすべての要素を含む場合、その集合は別の集合の超集合です。これは部分集合の逆の関係です。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const declarativeLanguages = new Set(["HTML", "CSS"]);
declarativeLanguages.isSupersetOf(frontEndLanguages);
// => false
frontEndLanguages.isSupersetOf(declarativeLanguages);
// => true
任意の集合は自分自身の超集合です。
frontEndLanguages.isSupersetOf(frontEndLanguages);
// => true
Set.prototype.isDisjointFrom(other)#
二つの集合に共通の要素がない場合、それらは交差しません。
const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const interpretedLanguages = new Set(["JavaScript", "Ruby", "Python"]);
const compiledLanguages = new Set(["Java", "C++", "TypeScript"]);
interpretedLanguages.isDisjointFrom(compiledLanguages);
// => true
frontEndLanguages.isDisjointFrom(interpretedLanguages);
// => false
これらの例では、解釈型言語とコンパイル型言語の集合には交差がないため、これらの集合は交差しません。一方、フロントエンド言語と解釈型言語の集合は「JavaScript」という共通の要素があるため、交差しないわけではありません。
サポート状況#
この記事執筆時点で、これらの新しい Set
メソッドの 提案 は TC39 の標準化プロセスの第 3 段階に入り、Safari 17(2023 年 9 月リリース)および Chrome 122(2024 年 2 月)でこれらのメソッドが実装されました。Edge は Chrome に続いてリリースされ、Firefox Nightly は実験的なフラグの後にこれらの機能をサポートしており、これらのブラウザもすぐに正式にサポートされる見込みです。
Bun もまた Safari の JavaScriptCore エンジンを採用しているため、これらの新機能をサポートしています。Chrome のこれらの機能のサポートは、これらが V8 JavaScript エンジンに統合され、近く Node.js にも採用されることを意味します。
これが、これらの提案がプロセスの第 4 段階にスムーズに移行し、タイムリーに ES2024 規格の一部となることを期待しています。
ポリフィル#
古い JavaScript エンジンでこれらの機能をサポートする必要がある場合は、ポリフィルを使用できます。これらのポリフィルは core-js から入手できるか、es-shims プロジェクトの個別パッケージとして提供されています(例えば、和集合機能を実装するための set.prototype.union パッケージ)。
これらの機能のために独自の実装を作成している場合は、まずこれらのポリフィルに移行し、これらの機能が広くサポートされるにつれて、カスタム実装を段階的に廃止することをお勧めします。
Sets の機能が欠けているとは感じなくなった#
JavaScript Set
は長い間機能が不完全と見なされてきましたが、これらの七つの新しい関数の追加により、その機能はより充実しました。このような機能が言語自体に組み込まれることは、外部依存や独自実装の必要性を減らし、実際の問題解決により集中できることを意味します。
これは TC39 が現在審議している多くの第 3 段階提案の一部に過ぎません。このリストを確認して、JavaScript に今後追加される可能性のある新機能を把握してください。特に Temporal と Decorators に注目しており、これらの提案は私たちが JavaScript を記述する重要な部分の方法を変える可能性があります。