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