集合是由一组无序且唯一(即不能重复)的项组成的。
创建集合类
class Set {
constructor() {
this.items = {};
}
//首先要实现的是 has(element)方法,因为它会被 add、delete 等其他方法调用。如果元素在集合中,返回 true,否则返回 false
has(element) {
return Object.prototype.hasOwnProperty.call(this.items, element); //Object 原型有 hasOwnProperty 方法。该方法返回一个表明对象是否具有特定属性的布尔
值。或者使用eturn element in items;,in 运算符则返回表示对象在原型链上是否有特定属性的布尔值。r
}
//向集合添加一个新元素
add(element) {
if (!this.has(element)) {
this.items[element] = element; //添加一个 element 的时候,把它同时作为键和值保存,因为这样有利于查找该元素。
return true;
}
return false;
}
//向集合移除一个新元素
delete(element) {
if (this.has(element)) {
delete this.items[element];
return true;
}
return false;
}
//返回一个包含集合中所有值(元素)的数组
values() {
return Object.values(this.items);//Object.values()方法返回了一个包含给定对象所有属性值的数组。它是在 ECMAScript 2017中被添加进来的,目前只在现代浏览器中可用。
}
//如果想让代码在任何浏览器中都能执行,可以用与之前代码等价的下面这段代码。
valuesLegacy() {
let values = [];
for(let key in this.items) { // 首先迭代 items 对象的所有属性
if(this.items.hasOwnProperty(key)) {
values.push(key); // 添加到一个数组中
}
}
return values;
};
//并集
union(otherSet) {
const unionSet = new Set();//首先需要创建一个新的集合,代表两个集合的并集
this.values().forEach(value => unionSet.add(value));//获取第一个集合(当
前的 Set 类实例)所有的值(values),迭代并全部添加到代表并集的集合中
otherSet.values().forEach(value => unionSet.add(value));
return unionSet;
}
//交集
intersection(otherSet) {
const intersectionSet = new Set();
const values = this.values();
const otherValues = otherSet.values();
let biggerSet = values;
let smallerSet = otherValues;
if (otherValues.length - values.length > 0) {//比较两个集合的元素个数(行{6}), 如果另一个集合元素个数多于当前集合的话,我们就交换 biggerSet 和 smallerSet 的值。最 后,迭代较小集合(行{7})来计算出两个集合的共有元素并返回。
biggerSet = otherValues;
smallerSet = values;
}
smallerSet.forEach(value => {
if (biggerSet.includes(value)) {
intersectionSet.add(value);
}
});
return intersectionSet;
}
//差集:存在于 A中,且 x不存在于 B中
difference(otherSet) {
const differenceSet = new Set();
this.values().forEach(value => {
if (!otherSet.has(value)) {
differenceSet.add(value);
}
});
return differenceSet;
}
//子集
isSubsetOf(otherSet) {
if (this.size() > otherSet.size()) {
return false;//如果当前实例中的元素比 otherSet 实例更多,它就不是一个子集。子集的元素个数需要小于或等于要比较的集合
}
let isSubset = true;
this.values().every(value => {
if (!otherSet.has(value)) {
isSubset = false;
return false;
}
return true;
});
return isSubset;
}
isEmpty() {
return this.size() === 0;
}
//返回集合所包含元素的数量。它与数组的 length 属性类似
size() {
return Object.keys(this.items).length;//使用 JavaScript中 Object 类的一个内置方法,返回一个包含给定对象所有属性的数组(ECMAScript 2015以上版本)
}
//第二种方式是手动提取 items 对象的每一个属性,记录属性的个数并返回这个数
sizeLegacy() {
let count = 0;
for(let key in this.items) {
if(this.items.hasOwnProperty(key)) { // 检查它们是否是对象自身的属性(避免重复计数)
count++;
}
}
return count;
};
//移除所有元素
clear() {
this.items = {};
}
toString() {
if (this.isEmpty()) {
return '';
}
const values = this.values();
let objString = `${values[0]}`;
for (let i = 1; i < values.length; i++) {
objString = `${objString},${values[i].toString()}`;
}
return objString;
}
}
本章实现的union、intersection 和difference 方法不会 修改当前的 Set 类实例或是作为参数传入的 otherSet。没有副作用的方法和函 数被称为纯函数。纯函数不会修改当前的实例或参数,只会生成一个新的结果。
ECMAScript 2015——Set 类
ES2015的 Set 的 values 方法返回 Iterator(第 3章提到过),而不是值构成的数组。
我们实现的 size 方法返回 set 中存储的值的个数,而 ES2015 的 Set 则有一个 size 属性。
我们可以用 delete 方法删除 set 中的元素。 set.delete(1);
ES2015原生的 Set 并没有并集、交集、差集、子集这些功能
const union = (setA, setB) => {
const unionAb = new Set();
setA.forEach(value => unionAb.add(value));
setB.forEach(value => unionAb.add(value));
return unionAb;
};
console.log(union(setA, setB)); // 输出[1, 2, 3, 4]
const intersection = (setA, setB) => {
const intersectionSet = new Set();
setA.forEach(value => {
if (setB.has(value)) {
intersectionSet.add(value);
}
});
return intersectionSet;
};
console.log(intersection(setA, setB)); // 输出[2, 3]
const difference = (setA, setB) => {
const differenceSet = new Set();
setA.forEach(value => {
if (!setB.has(value)) { //
differenceSet.add(value);
}
});
return differenceSet;
};
console.log(difference(setA, setB));
有一种计算并集、交集和差集的简便方法,就是使用扩展运算符
整个过程包含三个步骤:
(1) 将集合转化为数组;
(2) 执行需要的运算;
(3) 将结果转化回集合。
用扩展运算符进行并集的计算:
console.log(new Set([...setA, ...setB]));
//ES2015的 Set 类支持向构造函数传入一个数组来初始化集合的运算,那么我们对 setA 使用扩展运算符(...setA)会将它的值转化为一个数组(展开它的值),然后对 setB 也这样做。
//由于 setA 的值为[1, 2, 3],setB 的值为[2, 3, 4],上述代码和 new Set([1, 2, 3,
2, 3, 4])是一样的,但集合中每种值只会有一个。
用扩展运算符进行交集的运算。
console.log(new Set([...setA].filter(x => setB.has(x))));
//将 setA 转化为了一个数组,并使用了 filter 方法,它会返回一个新数组,
包含能通过回调函数检测的值——在本示例中验证了元素是否也存在于 setB 中。返回的数组会 用来初始化结果集合。
用扩展运算符完成差集的运算。
console.log(new Set([...setA].filter(x => !setB.has(x))));