Setに加わった「集合論的」なメソッド(2)
2つの集合の対称差を得る――symmetricDifferenceメソッド
symmetricDifferenceメソッドは、2つの集合の対称差(A △ B)を計算して新たなSetオブジェクトを返します。対称差とは、2つの集合のどちらか片方にしか存在しない要素です。2つの名簿を比べてどちらか一方にしか乗っていない人のリストを作ったり、変更前と変更後の差分リストを作る、といったときに利用できます。
以下のリストは、2つの集合の対称差を求めるものです。
const gym = new Set(['鈴木', '山田', '田中', '佐藤', '高橋']);
const pool = new Set(['伊藤', '渡辺', '山本', '田中', '佐藤']);
const symmetricDifference = new Set([...gym]
.filter(x => !pool.has(x)) // gymからpoolにないものを抽出
.concat([...pool] // その結果にpoolを追加
.filter(x => !gym.has(x)))); // その結果からgymにないものを抽出
console.log(`ジムかプールのどちらかにしか所属していない生徒は ${symmetricDifference.size} 名です。`);
console.log(`ジムかプールのどちらかにしか所属していない生徒は ${gym.symmetricDifference(pool).size} 名です。`);
// ジムかプールのどちらかにしか所属していない生徒は 6 名です。
symmetricDifferenceメソッドを使わない場合、filterメソッドを2回、concatメソッドを1回呼び出す必要があり、見た目に煩雑ですし、処理時間もかかります。symmetricDifferenceメソッドを使えば行おうとしていることが一目瞭然です。
部分集合であるか判定する――isSubsetOfメソッド
ここからは、判定メソッドを紹介します。isSubsetOfメソッドは、集合Aが集合Bの部分集合(A ⊆ B)であるかを判定して、結果を論理値で返します。部分集合とは、集合Aの要素すべてを集合Bが含むという関係です。たとえば、ユーザーが持つ権限が必要権限を満たしているか確認するときなどに利用できます。
以下のリストは、2つの集合がお互いに部分集合関係にあるかを調べるものです。
const required = new Set(["read", "write"]);
const user1 = new Set(["read", "write", "delete"]);
const user2 = new Set(["read"]);
const isSubsetOfMethod = (subSet, superSet) => [...subSet]
.every((value) => superSet.has(value)); // すべてがsuperSetにあるか確認
console.log(`ユーザー1は権限を満たして${isSubsetOfMethod(required, user1)? 'います。':'いません。'}`);
// ユーザー1は権限を満たしています。
console.log(`ユーザー2は権限を満たして${isSubsetOfMethod(required, user2)? 'います。':'いません。'}`);
// ユーザー2は権限を満たしていません。
console.log(`ユーザー1は権限を満たして${required.isSubsetOf(user1)? 'います。':'いません。'}`);
// ユーザー1は権限を満たしています。
console.log(`ユーザー2は権限を満たして${required.isSubsetOf(user2)? 'います。':'いません。'}`);
// ユーザー2は権限を満たしていません。
ここでは、isSubsetOfメソッドを使わない例として、isSubsetOfMethod関数を定義しました。この関数は、2つのSetオブジェクトを受け取り、一方の全要素に対してそれがもう一方に存在するかをeveryメソッドで判定しています。成立すれば、部分集合と見なせるわけです。
上位集合であるか判定する――isSupersetOfメソッド
isSupersetOfメソッドはisSubsetOfメソッドの逆で、集合Aが集合Bの上位集合(A ⊇ B)であるかを判定して、結果を論理値で返します。上位集合とは、集合Aが集合Bの要素すべてを含むという関係です。
位置付け的には、isSubsetOfメソッドのインスタンスと引数を逆にしたものです。よって、同様の目的で利用できます。
以下のリストは、2つの集合がお互いに上位集合関係にあるかを調べるものです。
const required = new Set(["read", "write"]);
const user1 = new Set(["read", "write", "delete"]);
const user2 = new Set(["read"]);
const isSupersetOfMethod = (superSet, subSet) => [...subSet]
.every((value) => superSet.has(value)); // すべてがsuperSetにあるか確認
console.log(`ユーザー1は権限を満たして${isSupersetOfMethod(user1, required)? 'います。':'いません。'}`);
// ユーザー1は権限を満たしています。
console.log(`ユーザー2は権限を満たして${isSupersetOfMethod(user2, required)? 'います。':'いません。'}`);
// ユーザー2は権限を満たしていません。
console.log(`ユーザー1は権限を満たして${user1.isSupersetOf(required)? 'います。':'いません。'}`);
// ユーザー1は権限を満たしています。
console.log(`ユーザー2は権限を満たして${user2.isSupersetOf(required)? 'います。':'いません。'}`);
// ユーザー2は権限を満たしていません。
この通り、使い方はisSupersetOfメソッドと逆になります。また、ここでもisSupersetOfメソッドを使わない例としてisSupersetOfMethod関数を定義しました。行っていることは前項と変わらず、判定する集合を逆にしているだけです。
2つの集合が互いに素であるか判定する――isDisjointFromメソッド
最後のisDisjointFromメソッドは、2つの集合AとBが互いに素(A ⊥ B)であるかを判定して、結果を論理値で返します。互いに素であるとは、2つの集合が共通の要素を持たないことを意味します。たとえば、ユーザーのロールが競合しないことを確認するときなどに利用できます。
以下のリストは、3つの集合が互いに素であるかを調べるものです。
const adminRoles = new Set(["admin"]);
const bannedRoles = new Set(["banned"]);
const userRoles = new Set(["user", "banned"]);
const isDisjointFromMethod = (setA, setB) => [...setA]
.every((value) => !setB.has(value)); // すべてがsetBにないか調べる
console.log(`adminとbannedは競合${isDisjointFromMethod(adminRoles, bannedRoles)? 'していません。':'しています。'}`);
// adminとbannedは競合していません。
console.log(`adminとuserは競合${isDisjointFromMethod(adminRoles, userRoles)? 'していません。':'しています。'}`);
// adminとuserは競合していません。
console.log(`userとbannedは競合${isDisjointFromMethod(userRoles, bannedRoles)? 'していません。':'しています。'}`);
// userとbannedは競合しています。
console.log(`adminとbannedは競合${adminRoles.isDisjointFrom(bannedRoles)? 'していません。':'しています。'}`);
console.log(`adminとuserは競合${adminRoles.isDisjointFrom(userRoles)? 'していません。':'しています。'}`);
console.log(`userとbannedは競合${userRoles.isDisjointFrom(bannedRoles)? 'していません。':'しています。'}`);
// 出力は同じ
まとめ
今回は、ECMAScript 2025で導入されたNew Set methodsを紹介しました。ただの重複のないコレクションであったSetが、本来の役割としての集合論的な機能を持つオブジェクトに変化したことをお伝えすることができたのではないかと思います。
次回からは、ECMAScriptで最近標準化された有用な機能をいくつかピックアップし、紹介していきます。
