ES6 新增了 Set 集合,集合内部元素不可重复,Set 大多数 API 和 行为 与 Map 是共有的,更像是 Map 的加强版
Set 思维导图
创建集合
集合内的元素根据 SameValueZero(即严格相等标准) 来去除重复的元素
const s1 = new Set();
// add 方法返回的仍然是 Set 实例,这里虽然添加了 两次 v2 值,但是 Set 是集合,
// 元素不可重复, 添加两次 {name: "jakequc"} 成功的原因是两个对象地址不相等
const obj = { age: 23 };
s1.add("v1")
.add("v2")
.add("v2")
.add({ name: "jakequc" })
.add({ name: "jakequc" })
.add(obj)
.add(obj); // 虽然 add 了两次 obj ,但是只会添加成功一次
log(s1); // log: Set(4) { 'v1', 'v2', { name: 'jakequc' }, { name: 'jakequc' }, { age: 23 } }
log(s1.has("v1")); // log: true
const s2 = new Set([
["k1", "v1"],
["k2", "v2"],
]);
log(s2); // log: Set(2) { [ 'k1', 'v1' ], [ 'k2', 'v2' ] }
log(s2.has("k1")); // false
const s3 = new Set({
[Symbol.iterator]: function* () {
yield "v1";
yield "v2";
},
});
log(s3); // Set(2) { 'v1', 'v2' }
const s = new Set();
const objVal = {};
const arrVal = [];
s.add(objVal).add(arrVal);
(objVal.name = "jakequc"), arrVal.push("arrItem");
log(s.has(objVal)); // log: true
log(s.has(arrVal)); // log: true
顺序与迭代
Set 会维护值插入时的顺序,因此支持按顺序迭代。集合实例提供了一个迭代器(能以插入顺序生成集合内容,可以通过 values() 方法以及其 keys() 或者 Symbol.iterator 属性来取得找个迭代器, values() 是默认迭代器,所以可以直接对集合使用扩展运算符的时候把集合转化为数组
const s = new Set(["v1", "v2", "v3", { v4: "v4" }]);
log(s.keys === s.values); // log: true, 表明他们都是同一个迭代器
log(s.keys === s[Symbol.iterator]); // log: true, 表明他们都是同一个迭代器
log(s.values === s[Symbol.iterator]); // log: true, 表明他们都是同一个迭代器
for (const v of s.values()) {
log(v); // 依此 打印 v1,v2,v3 值
}
const arr = [...s];
log(JSON.stringify(arr)); // log: ["v1","v2","v3"]
// entries 会返回一个 IterableIterator<[any,any]> 的类型,即以 实例包含的 值是 key 也是 value
const pairs = s.entries();
log(pairs);
s.forEach((val, dupVal) =>
log(`${JSON.stringify(val)} -> ${JSON.stringify(dupVal)}`)
);
/**
依此打印
"v1" -> "v1"
"v2" -> "v2"
"v3" -> "v3"
{"v4":"v4"} -> {"v4":"v4"}
*/
修改集合中的对象属性也会修改 set 的对象值
const valObj = { id: 1 };
const s2 = new Set([valObj]);
log(s2); // log: Set(1) { { id: 1 } }
for (const obj of s2.values()) {
obj.id = "333";
log(s2.has(obj)); // true
}
log(s2); // log: Set(1) { { id: '333' } }
定义一个集合操作
- 实现一个集合操作 XSet,实现如下功能
- 集合操作,支持处理任意多个集合实例
- 保证插入顺序
- 尽可能高效使用内存,扩展操作符的语法很简洁,但是尽可能避免集合和数组间的相互转换,能够节省对象初始化成本
- 不要修改已有的集合实例
- 实现并集操作
class XSet extends Set {
// 返回两个或更多集合的交集
static union(a, ...otherSets) {
const unionSet = new Set(a);
for (const otherSet of otherSets) {
for (const otherVal of otherSet) {
unionSet.add(otherVal);
}
}
return unionSet;
}
// 返回两个或更多集合的交集(即多个集合间都存在的集合)
static intersection(a, ...otherSets) {
const intersectionSet = new XSet(a);
for (const intersectionVal of intersectionSet) {
for (const otherSet of otherSets) {
if (!otherSet.has(intersectionVal)) {
// 如果 otherSet 中没有这个值,交集集合中删除这个值
intersectionSet.delete(intersectionVal);
}
}
}
return intersectionSet;
}
// 返回两个集合中的差集
static difference(a, b) {
const differenceSet = new XSet(a);
for (const bValue of b) {
if (a.has(bValue)) {
differenceSet.delete(bValue);
}
}
return differenceSet;
}
/**
* 对称差集: 集合论中的数学术语,即两个集合的对称差是只属于其中一个集合,而不属于另一个集合的元素组成的集合。
* 集合论中的这个运算相当于布尔逻辑中的 异或运算。
*
* 因此对称差集等于 (a 与 b 的并集结果) 和 (a 与 b 的交集结果) 进行差集
* 集合 A 和 B 的对称差通常表示为 AΔB。 例如:集合 {1,2,3} 和 {3,4} 的对称差为 {1,2,4
* @param {*} a 集合 a
* @param {*} b 集合 b
* @returns a,b 集合的 对称差集
*/
static symmetricDifference(a, b) {
return a.union(b).difference(a.intersection(b));
}
// 返回两个集合(数组对形式)的笛卡尔积
// 必须返回数组集合,因为笛卡尔积可能包含相同值的对
static cartesianProduct(a, b) {
const cartesianProductSet = new XSet();
for (const aValue of a) {
for (const bValue of b) {
cartesianProductSet.add([aValue, bValue]);
}
}
return cartesianProductSet;
}
/**
* 返回一个集合的幂集
* 所谓幂集(Power Set), 就是原集合中所有的子集(包括全集和空集)构成的集族
* [1, 2, 3, 4]
*/
static powerSet(a) {
const emptySet = new XSet();
// powerSet 的每个元素 都是一个集合
const powerSet = new XSet().add(emptySet);
for (const currentVal of a) {
const prePowerSet = new XSet(powerSet);
for (const preSubSet of prePowerSet) {
// 之前的子集 加上当前的 值 构成新的子集
const newSubSet = new XSet(preSubSet).add(currentVal);
powerSet.add(newSubSet);
}
}
return powerSet;
}
// 实现实例的并集操作
union(...sets) {
return XSet.union(this, ...sets);
}
// 实现实例的交集操作
intersection(...sets) {
return XSet.intersection(this, ...sets);
}
// 差集
difference(set) {
return XSet.difference(this, set);
}
// 对称差集
symmetricDifference(set) {
return XSet.symmetricDifference(this, set);
}
// 笛卡尔积
cartesianProduct(set) {
return XSet.cartesianProduct(this, set);
}
// 幂集,所以子集
powerSet() {
return XSet.powerSet(this);
}
}
const a = new XSet([1, 2, 3, 4]);
const b = new XSet([4, 5, 6, 7, 8, 9]);
const c = new XSet([10, 11]);
// a,b,c 的并集
log(a.union(b, c)); // log: Set(11) { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }
// a,b 的交集
log(a.intersection(b)); // log: XSet(2) [Set] { 4, 5 }
// a,b 集合的差集 所有属于A且不属于B的元素构成的集合,叫做集合A减集合B(或集合A与集合B之差)
log(a.difference(b)); // log: XSet(3) [Set] { 1, 2, 3 }
// a,b 的笛卡尔积
log(a.cartesianProduct(b));
// a 的幂集(所有子集)
log(a.powerSet());