JavaScript Sets中的并集、交集、差集

193 阅读6分钟

image.png

Set 在ES2015规范中被引入到语言中,但它总是看起来不完整。这种情况即将改变。

在ES2015版本的 Set 中,可用的方法:创建、添加、删除和检查 Set 的成员关系。如果要操作或比较多个集合,就必须编写自己的函数。值得庆幸的是,TC39(ECMAScript规范建立的委员会)和浏览器厂商一直在为此努力。

在我们看新功能之前,让我们回顾一下JavaScript Set可以做什么?

ES2015 JavaScript集做什么?

让我们来看看ES2015版本的JavaScript  Set 可以做些什么。

您可以不带任何参数构造 Set ,这将创建一个空的 Set 。或者,使用数组,来初始化 Set 。

const languages = new Set(["JavaScript", "TypeScript", "HTML", "JavaScript"]);

Set 只能包含唯一的值,因此上面的 Set 有三个成员。可以使用 size 属性检查这一点。

languages.size; 
// => 3

可以使用 add 函数向 Set 中添加更多元素。添加一个已经存在 Set 中的元素不会做任何事情。

languages.add("JavaScript"); 
languages.add("CSS"); 
languages.size; 
// => 4

可以使用 delete 从 Set 中删除元素。

languages.delete("TypeScript");
languages.size;
// => 3

可以使用 has 函数检查元素是否为 Set 的成员。 Set 的一个好处是,这种检查可以在O(1)内完成,而检查元素是否在数组中,则随数组的长度O(n)而变化。

languages.has("JavaScript"); 
// => true languages.has("TypeScript"); 
// => false

可以使用 forEach 或 for...of 循环遍历 Set 的元素。元素按照它们加入 Set 的顺序排序。

languages.forEach(element => console.log(element)); 
// "JavaScript" 
// "HTML" 
// "CSS"

最后,可以使用 clear 函数清空Set。

languages.clear(); 
languages.size; 
// => 0

总结:可以用ES2015规范版本的 Set 做什么:

  • Set 提供了处理唯一值集合的方法
  • 将 Array 或其他可迭代对象转换为 Set 是过滤重复项的简单方法

但是,这些实现忽略了 Set 之间的操作。您可能想要创建一个 Set ,它包含来自其他两个 Set 的所有项(两个 Set 的并集),找出两个 Set 有什么共同点(交集),或者找出在一个集合中不存在而在另一个集合中存在的东西(差集)。

新的Set函数是什么?

新的Set 方法添加以下方法实例: union ,  intersection ,  difference ,  symmetricDifference ,  isSubsetOf ,  isSupersetOf 和 isDisjointFrom 。

其中一些方法类似于一些SQL连接,我们将使用它们来演示结果和代码。让我们看看每个函数做什么的一些例子。

您可以在Chrome 122或Safari 17中尝试下面的任何代码示例。

Set.prototype.union()

集合的并集是包含任意一个集合中存在的所有元素的集合。

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"}

在本例中,前两个集合中的所有语言都在第三个集合中。

这相当于两个表之间的SQL  FULL OUTER JOIN 。

image.png

Set.prototype.intersection()

交集是包含两个集合中存在的所有元素的集合。

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]); 
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]); 
const frontAndBackEnd = frontEndLanguages.intersection(backEndLanguages); 
// => Set {"JavaScript"}

“JavaScript”是这里两个集合中出现的共同元素。

交集就像一个 INNER JOIN 。

image.png

Set.prototype.difference()

正在处理的集合和另一个集合之间的区别是所有元素在第一个集合中出现,而在第二个集合中没有出现。

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”。

差异就像执行 LEFT JOIN 操作。

image.png

Set.prototype.symmetricDifference()

两个集合之间的对称差是一个包含其中一个集合中的所有元素,但不包含两个集合中的所有元素的集合。

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"}

在这种情况下,结果集中的元素是相同的,但请注意顺序不同。集合顺序由元素添加到集合的顺序决定,在其上执行函数的集合将首先添加其元素。

对称差异类似于排除两个表中的相同元素的FULL OUTER JOIN。

image.png

Set.prototype.isSubsetOf()

如果第一个集合中的所有元素都出现在第二个集合中,则该集合是另一个集合的子集。

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()

如果第二个集合中的所有元素都出现在第一个集合中,则该集合是另一个集合的超集。它与子集的关系是相反的。

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()

最后,如果一个集合与另一个集合没有共同的元素,则它们是不相交的。

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”重叠,因此它们并不是分离的。

支持

在撰写本文时,该提案处于TC39进程的第三阶段,Safari 17(2023年9月发布)和Chrome 122(2024年2月)已经发布了这些方法的实现。Edge紧随Chrome, Firefox Nightly也有支持,我预计这两款浏览器也会很快发布支持。

Bun使用了Safari的JavaScriptCore引擎,因此已经支持了这些新功能。Chrome的支持意味着它已经被添加到V8 JavaScript引擎中,并将很快被Node.js采用。

希望该提案将进入流程的第4阶段,甚至可能在最终确定之前及时加入ES2024规范。

Polyfills

虽然您需要旧的JavaScript引擎支持,可以使用polyfill将这些函数升级到符合规范的实现。它们可以在core-js中使用,也可以作为es-shims项目中每个函数的单独包使用(例如,set.prototype.union包可用于union功能)。

如果您已经为这些函数中的任何一个编写了自己的实现,我建议首先升级到polyfills,然后随着支持变得更加广泛而逐步淘汰它们。

原文:Union, intersection, difference, and more are coming to JavaScript Sets | Sonar (sonarsource.com)