JavaScript Sets中关于union、intersection、difference等功能介绍

170 阅读6分钟

image.png JavaScript Set 是在 ES2015 规范中引入该语言的,但它似乎总是不完整的。这种情况即将改变。

集合是值的集合,其中每个值只能出现一次。在 ES2015 版本的 Set 中,可用功能主要围绕创建、添加到 Set 中、从 中删除以及检查 的成员资格。如果您想对多个集合进行操作或比较,就必须编写自己的函数。值得庆幸的是,为制定 ECMAScript 规范而成立的 TC39 委员会和浏览器一直在努力解决这一问题。我们现在可以在 JavaScript 实现中看到 union 、intersection 和 difference 等函数。

在了解新功能之前,让我们先回顾一下 JavaScript Sets 现在能做什么,然后再了解新的 Set 功能和支持这些功能的 JavaScript 引擎。

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))。对于这样的任务,使用 Set 是一种编写有意义且高效的代码的清晰方式。

languages.has("JavaScript");

// => true

languages.has("TypeScript");

// => false

您可以使用 forEach 或 循环for...of遍历 Set 的元素。元素按照它们添加到 Set。

languages.forEach(element => console.log(element));

// "JavaScript"

// "HTML"

// "CSS"
for (let element of languages) { console.log(element); }

您还可以使用 keys 和 values 函数(实际上是等效的)以及entries函数获取Set迭代器。

keys()

keys() 方法返回一个新的迭代器对象,包含 Set 对象中的每个元素的值。在 Set 中,由于没有键,所以 keys() 和 values() 的行为是相同的。

let mySet = new Set([1, 2, 3]);
let keysIterator = mySet.keys();

console.log(keysIterator.next().value); // 1
console.log(keysIterator.next().value); // 2
console.log(keysIterator.next().value); // 3

values()

values() 方法返回一个新的迭代器对象,包含 Set 对象中的每个元素的值。

let mySet = new Set([1, 2, 3]);
let valuesIterator = mySet.values();

console.log(valuesIterator.next().value); // 1
console.log(valuesIterator.next().value); // 2
console.log(valuesIterator.next().value); // 3

entries()

entries() 方法返回一个新的迭代器对象,包含 Set 对象中每个元素的 [value, value] 数组。这是为了与 Map 对象的 API 保持一致。

let mySet = new Set([1, 2, 3]);
let entriesIterator = mySet.entries();

console.log(entriesIterator.next().value); // [1, 1]
console.log(entriesIterator.next().value); // [2, 2]
console.log(entriesIterator.next().value); // [3, 3]

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

languages.clear();

languages.size;

// => 0

这很好地提醒了您可以使用 ES2015 规范版本的 a Set 做什么:

  • Set提供处理唯一值集合的方法
  • 将元素添加到Set 并测试它们是否在Set
  • 将一个数组或其他可迭代对象转换为一个Set 是过滤掉重复项的一种简单方法

不过,此实现错过了 Set s 之间的操作。您可能想要创建一个包含来自另外两个 Set s 所有项目的 Set(两个 Set s 的并集),找出两个 Set s 共有的部分(交集),或者找出一个 Set 中不存在但在另一个中存在的内容(差异)。直到最近,您都必须提供自己的函数。

有哪些新的 Set 函数?

方法Set建议将以下方法添加到Set实例中: union 、 、 intersection 、 difference symmetricDifference isSubsetOf isSupersetOf 和 isDisjointFrom 。

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

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

image.png

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

image.png

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 "是这两组元素中唯一存在的元素。

交叉点就像一个 INNER JOIN 。

image.png

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

差异就像执行 LEFT JOIN 。

image.png

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

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

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

image.png

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”元素重叠,因此它们并不是不相交的。

Polyfills

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

参考:www.sonarsource.com/blog/union-…