集合是由一组无序且唯一(即不能重复)的项组成的。

创建集合类

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))));

 posted on 2020-11-17 14:35  chen_coder  阅读(108)  评论(0)    收藏  举报