模拟实现ES2015集合-Set类及运算

64 阅读3分钟

什么是集合

集合是由一组无序唯一的项组成的。该数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中。

在数学概念中,集合是一组不同对象的集。比如:一个大于或等于0的整数组成的自然数集合:N = {0, 1, 2, 3, 4, …}。集合中的对象列表用花括号包围。

还有一个概念叫空集。空集就是不包含任何元素的集合。空集用{}表示。

Set类的具体实现

言归正传ECMAScript2015介绍了Set类是JavaScript API的一部分,我将基于ES2015的Set类实现一套Set类,并提供一些在ES2015中没有的定义的集合运算,例如:并集、交集和差集;

    /**
     *声明Set类以及他的构造函数
     *利用对象存储数据
     */
    class Set{
        constructor() {
            this.items = {};
        }
    }

默认结合中应该有一些内置方法(模拟ECMAScript2015实现相同的Set类)

  • add(element): 向集合添加一个新元素
  • delete(element):从集合移除一个元素
  • has(element):如果元素在结合中,返回true,否则返回false
  • clear():移除结合中的所有元素
  • size(): 返回集合所包含元素的数量。它与数组的length属性类似
  • values():返回一个包含集合中所有值(元素)的数组

has(element)方法

首先要实现的是has(element)方法,因为他会被add、delete等其他方法调用。它用来检验某个元素是否存在于集合中。

    has(element) {
        return element in this.items;
    }

然后这个方法还有更好的实现方法。

    has(element) {
        return Object.prototype.hasOwnProperty.call(this.items, element);
    }

我们也可以在代码中使用this.items.hasOwnProperty(element)。但是,如果这样的话,代码检查工具如ESLint会抛出一个错误。错误的原因为不是所有的对象继承了Object.prototype,甚至继承了Object.prototype的对象上的hasOwnProperty方法也有可能被覆盖,导致代码不能支撑工作。要避免出现任何问题,使用Object.hasOwnProperty.call是更安全的做法。

add方法

    add(element) {
        if (!this.has(element)) {
            this.items[element] = element;
            return true;
        }
        return false;
    }

添加一个element时,将它同事作为键和值保存,因为这样有利于查找该元素

delete和clear方法

    delete(element) {
        if (this.has(element)) {
            delete this.items[element];
            reutrn true;
        }
        return false;
    }

    clear() {
        this.items = {};
    }

size方法

    size() {
        //ECMAScript2015以上版本
        return Object.keys(this.items).length;
    }

    sizeLegacy() {
        //浏览器的兼容性更高的写法
        let count = 0;
        for (let key in this.items) {
            if (this.items.hasOwnProperty(element)) {
                count ++;
            }
        }
        return count;
    }

values方法

    values() {
        //ECMAScript2015以上版本
        return Object.values(this.items);
    }

    valuesLegacy() {
        //浏览器的兼容性更高的写法
        let values = [];
        for (let key in this.items) {
            if (this.items.hasOwnProperty(key)) {
                values.push(key);
            }
        }
        return values;
    }

集合运算的实现(重点)

  • 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
  • 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
  • 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在与第二个集合的元素的新集合。
  • 子集:验证一个给定集合是否是另一个集合的子集。

并集

    union(otherSet){
        //ECMAScript2015以上版本
        const unionSet = new Set();
        this.values().forEach(value => unionSet.add(value));
        otherSet.values().forEach(value => unionSet.add(value));
        return unionSet;
    }

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

交集

    intersect(otherSet) {
        const intersectSet = new Set();

        const values = this.values();
        for (let i = 0; i < values.length; i++) {
            if (otherSet.has(values[i])) {
                intersectSet.add(values[i]);
            }
        }
        return intersectSet;
    }

优化后

    intersect(otherSet) {
        const intersetSet = 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)) {
                intersetSet.add(value);
            }
        })
        return intersetSet;
    }

差集

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;
        }
        let isSubset = true;
        this.values().every(value => {
            if (!otherSet.has(value)) {
                isSubset = false;
                return false;
            }
            return true;
        });
        return isSubSet;
    }