class Set { constructor() { this.items = {}; } }
这里使用对象来实现,但也可以使用数组。
JavaScript对象不允许一个键指向俩个不同的属性,保证了集合里的元素都是唯一的。
一些常用方法:
has(elemenet)方法会被add、delete方法调用,先实现它。
has(element) { return element in this.items; }
has(element) { return Object.prototype.hasOwnProperty.call(this.items, element); }
- 第二个方法返回一个表明对象是否具有特定属性的布尔值;in运算符则返回表示对象在原型链上是否有特定属性的布尔值。
- JavaScript中Object对象原型上的hasOwnProperty()用来判断一个属性是定义在对象本身而不是继承自原型链
add(element) { // 检查元素,不存在则添加到集合中 if (!this.has(element)) { // 添加一个元素时,将它同时作为键和值保存,有利于查找该元素 this.items[element] = element; return true; } return false; }
delete(element) { if (this.has(element)) { // 使用对象来存储集合的items对象,就可以简单使用delete运算符从items对象中移除属性 delete this.items[element]; return true; } return false; }
clear() { this.items = {}; }
移除元素可以直接将对象置空。也可以用delete方法依次移除所有元素,但没必要。
// keys对象返回一个包含给定对象所有属性的数组 size() { return Object.keys(this.items).length; }
size() { let count = 0; // 迭代items对象所有属性 for (let key in this.items) { // 检查它们是否是对象自身的属性(避免重复计数 if (this.items.hasOwnProperty(key)) { count++; } } return count; }
// Object.values()是JavaScript7加进来的,只能部分浏览器使用 values() { return Object.values(this.items); }
以下代码任何浏览器都能使用:
value() { let values = []; for (let key in this.items) { if (this.items.hasOwnProperty(key)) { values.push(key); } } return values; }
const set = new Set(); set.add(10); console.log(set.values()); console.log(set.has(1)); console.log(set.size()); set.add(20); console.log(set.values()); console.log(set.has(1)); console.log(set.size()); set.delete(10); console.log(set.values()); set.delete(20); console.log(set.values());
返回一个包含两个集合中所有元素的新集合。
union(otherSet) { // 创建一个新集合,代表两个集合的并集 const unionSet = new Set(); // 获取第一个集合的所有值,迭代并全部添加到并集的集合中 this.values().forEach(value => unionSet.add(value)); // 对第二个集合进行相同操作 otherSet.values().forEach(value => unionSet.add(value)); return unionSet; }
不使用mgforEach方法和箭头函数的版本:
union(otherSet) { const unionSet = new Set(); let values = this.values(); for (let i = 0; i < values.length; i++) { unionSet.add(values[i]); } values = otherSet.values(); for (let i = 0; i < values.length; i++) { unionSet.add(values[i]); } return unionSet }
测试代码:
const setA = new Set(); setA.add(1); setA.add(2); setA.add(3); const setB = new Set(); setB.add(3); setB.add(4); setB.add(5); setB.add(6); const unionAB = setA.union(setB); console.log(unionAB.values());
返回一个包含两个集合中共有元素的新集合。
intersection(otherSet) { const intersectionSet = new Set(); const values = this.values(); // 迭代当前Set实例所有的值 for (let i = 0; i < values.length; i++) { // 验证它们是否也存在otherSet中 if (otherSet.has(values[i])) { intersectionSet.add(values[i]); } } return intersectionSet; }
测试代码:
const intersectionAB = setA.intersection(setB); console.log(intersectionAB.values());
改进交集方法:
问题:假设存在以下两个集合
解决:先比较集合大小,再迭代较小的集合
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) { biggerSet = otherValues; smallerSet = values; } smallerSet.forEach(value => { if (biggerSet.includes(value)) { intersectionSet.add(value); } }); return intersectionSet; }
对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。
difference(otherSet) { const differenceSet = new Set(); this.values().forEach(value => { if (!otherSet.has(value)) { differenceSet.add(value); } }) return differenceSet; }
注意:
不能像优化intersection方法一样优化difference方法,因为setA和setB的差集可能不同。
验证一个给定集合是否是另一集合的子集。
isSubsetOf(otherSet) { // 当前实例的元素逼otherSet实例更多,就不是一个子集 if (this.size() > otherSet.size()) { return false; } // 假定当前实例是给定集合的子集 let isSubset = true; // 迭代当前集合的元素 this.values().every(value => { if (!otherSet.has(value)) { isSubset = false; return false; } }) return isSubset; }
ECMAJavascript2015新增了Set类。
区别: