# (五)Set

TIP

大家对这 Set 数据结构总是抱着一种抵触,因为他与 Array 结构很相似,这次我们就来看看他们拥有什么能力,以及使用场景有哪些,共勉!

# 一、set 的基本用法

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set 本身是一个构造函数,用来生成 Set 数据结构。

const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach((x) => s.add(x));

s; // Set(4) {2, 3, 5, 4}

# 二、Set 实例的方法和属性

# 2.1 属性

  • Set.prototype.constructor:构造函数,默认就是 Set 函数。
  • Set.prototype.size:返回 Set 实例的成员总数。

# 2.2 方法

Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为 Set 的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。

set.add()

s.add(1).add(2).add(2);
// 注意2被加入了两次

s.size; // 2

下面是一个对比,看看在判断是否包括一个键上面,Object 结构和 Set 结构的写法不同。

Array.from 方法可以将 Set 结构转为数组

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

这就提供了去除数组重复成员的另一种方法。

# 2.3 遍历操作

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

需要特别指出的是,Set 的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

# keys()、values()、entries()

由于 Set 结构没有键名,只有键值,所以 keys 方法和 values 方法的行为完全一致。

const set = new Set(["a", "b", "c", "d"]);
set.keys();
set.values();
// SetIterator {"a", "b", "c", "d"}
// 0: "a"
// 1: "b"
// 2: "c"
// 3: "d"

# forEach()

Set 结构的实例与数组一样,也拥有 forEach 方法,用于对每个成员执行某种操作,没有返回值。

let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + " : " + value));
// 1 : 1
// 4 : 4
// 9 : 9

# 遍历的应用

  • 扩展运算符(...)内部使用 for...of 循环,所以也可以用于 Set 结构。
  • 扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。

而且,数组的 map 和 filter 方法也可以间接用于 Set 了。

let set = new Set([1, 2, 3]);
set = new Set([...set].map((x) => x * 2));
// 返回 Set 结构:{2, 4, 6}

let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter((x) => x % 2 == 0));
// 返回 Set 结构:{2, 4}

因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = [...a, ...b];

// 交集
let intersect = [...a].filter((x) => b.has(x));

// (a 相对于 b 的)差集
let difference = [...a].filter((x) => !b.has(x));

如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用 Array.from 方法。

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map((val) => val * 2));
// set 的值是 2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, (val) => val * 2));
// set 的值是 2, 4, 6

上面代码提供了两种方法,直接在遍历操作中改变原来的 Set 结构。

# 三、set 的特点

# 3.1. Set 结构不会添加重复的值

const set = new Set([1, 2, 3, 4, 4]);
set.size; // 4
[...set]; // [1, 2, 3, 4]

# 3.2 Set 函数可以接受类数组作为初始化参数

// 类数组的对象作为参数。
const set = new Set(document.querySelectorAll("div"));
set.size; // 56

// 数组
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size; // 5
Last Updated: 2/15/2023, 7:45:22 PM