温泉里挣扎的set()方法

一直以来,JS 只能使用数组和对象来保存多个数据,缺乏像其他语言那样拥有丰富的集合类型。因此,ES6 新增了两种集合类型 setmap,用于在不同的场景中发挥作用。由于文章篇幅的原因,今天先聊聊 set,map 将在下一篇文章再去说。

「set 集合用于存放不重复的数据」

0、如何创建 set 集合

new Set();//创建一个没有任何内容的set集合
new Set(iterable);//创建一个具有初始内容的set集合,内容来自于可迭代对象每一次迭代的结果

例:创建没有内容的 set 集合

const result = new Set();
 console.log(result);

执行结果:

例:创建一个可迭代对象的集合

const result = new Set([1,3,3,4,5,5,6]);
 console.log(result);

执行结果:

在文章开头我们就提到set集合用于存放不重复的数据,所以最后输出结果就是不重复的数组,这也是一种非常简单的数组去重的方式。当然 Set(数据)的参数不一定是数组,只要是可迭代对象都可以,甚至可以是字符串,如下:

const result = new Set('abbccdde');
 console.log(result);

执行结果:

如果是字符串,它会先转换为String对象,String对象其实是可迭代对象,从执行结果来看Set集合不仅可以用于数组去重,也可以用于字符串去重

1、如何对set集合进行后续操作

「1、add(数据):添加一个数据到set集合末尾,如果数据已存在,则不进行任何操作」

例:使用add添加数据

 const result = new Set();
        result.add(1);
        result.add(2);
        result.add(3);
        result.add(1);//无效代码,Set内部会认为这条数据跟前面的数据重复
        result.add(2);//无效代码,Set内部会认为这条数据跟前面的数据重复
        console.log(result);

执行结果:

这里面有个细节大家要注意!下:set里面它是怎么判断两个数据是否相同呢? set使用Object.is的方式判断两个数据是否相同,而不是用===严格相等,但是,针对+0和-0,set认为它们是相等的,什么意思呢?其实是这样:

Object.is(+0,-0);//返回false
+0 === -0;//返回true

本来set是使用Object.is方式来判断数据是否相等,但set单独针对这块又把+0和-0看成相等的。代码测试:

 const result = new Set();
        result.add(1);
        result.add(2);
        result.add(3);
        result.add(1);//无效代码,Set内部会认为这条数据跟前面的数据重复
        result.add(2);//无效代码,Set内部会认为这条数据跟前面的数据重复
        result.add(+0);
        result.add(-0);//因此这段代码是无效的
        console.log(result);

执行结果:

本不应该出现这种情况的,之所以出现这个问题,纯粹是设计的问题,设计者估计是没办法开脱的,他不应该这样去设计,一个语言设计上它要统一。ES的设计不可能是一个人完成的,是很多人一起设计开发的,难免会造成不统一的情况。

还有一点就是如果判断的数据是对象是否相等,那么就要判断它的地址是否相等。

「2、has(数据):判断set中是否存在对应的数据」

由于set集合中已经使用add添加好数据,那么我直接使用has方法

执行结果:
温泉里挣扎的set()方法_第1张图片

「3、delete(数据):删除匹配的数据,返回是否删除成功」

执行结果:
温泉里挣扎的set()方法_第2张图片

如果删除存在的数据就会返回true,如果是删除不存在的数据,则返回false

「4、clear():没有参数,清空整个set集合」

执行结果:
温泉里挣扎的set()方法_第3张图片

2、如何与数组进行转换

例:数组转换成set集合

const result = new Set(要转换的数组)

例:set集合转换为数组

const arr = [...result];

接下来我们看一个小例子:要求就是把一个数组去重之后得到一个新的数组。

例:数组去重得到新的数组

var arr = [1,1,33,44,21,23,56,34,56,56,77,77];
const result =[...new Set(arr)];
console.log(result);

执行结果:

以上就是非常简单地数组去重方法

顺便写下字符串去重

例:字符串去重

var str = 'asndjfnajsknfdqwpsfdnsifnasnfdijsf';
const result =[...new Set(str)].join(" ");
console.log(result);

执行结果:

3、如何遍历

刚才我们已经说了,set本身是可迭代对象,因此可以用for of循环进行遍历。

  • 使用forof进行遍历
const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
 for (const item of result) {
      console.log(item);
}

执行结果:
温泉里挣扎的set()方法_第4张图片

  • 使用set中实例方法forEach
 const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
 result.forEach(item => {
      console.log(item);
})

执行结果:
温泉里挣扎的set()方法_第5张图片

使用forEach遍历我们要注意,在数组中进行遍历的时候,forEach是有三个参数的forEach(item,index,that),第一个参数是每一项的值,第二个参数是下标,第三个参数是数组本身。但是在set里面是有差别的,我们加上三个参数试试:

const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
 result.forEach((item,index,that) => {
      console.log(item,index,that);
  })

执行结果:
温泉里挣扎的set()方法_第6张图片

我们可以发现第一个参数和第三个参数没有问题,因为在数组中第一个参数也是每一项的值,第三个参数是数组本身,而第二个参数却不是下标,那为什么不是下标呢?

注意: set集合中不存在下标,因为下标是数组特有的东西,不要认为是集合特有的东西,比如,对象也是集合,里面可以存多个数据,对象就没有下标。因此在set集合中是不可能获取下标的,那自然而然不可能用普通for循环去循环它的下标,如果说一定要用下标的话,可以先把set集合转换为数组再使用它的下标。由于set集合中不存在下标,那么第二个参数就不是下标了。

但forEach又要保持格式统一性,之所以要保持格式统一是因为我们有可能会写一些通用的回调函数既适合数组调用,又适合set集合,为了保证通用性,因此set集合中的forEach仍然会保留第二个参数,只不过第二个参数跟第一个参数是一样的,都表示集合中的每一项。

「set集合中不存在下标,因此forEach中的回调的第二个参数和第一个参数是一致的,均表示set中的每一项。」

4、set集合的应用

求两个数组的并集、交集、差集 (不能出现重复项),得到的结果是一个新数组

例:求并集

const arr1 = [22,33,55,33,11,5,6];
const arr2 = [22,55,77,88,88,99];
//方法一:
const result = [...new Set(arr1.concat(arr2))];
console.log("并集",result);
//方法二:
const result = [...new Set([...arr1,...arr2])];
console.log("并集:",result);

执行结果:

例:求交集

const arr1 = [22,33,55,33,11,5,6];
const arr2 = [22,55,77,5,88,99];
//方法一:
const s1 = new Set(arr1);
const s2 = new Set(arr2);
const result = [...s1].filter(item => s2.has(item));
console.log("交集:",result);
//方法二
const s = new Set(arr1);
const result = [...s].filter(item => arr2.indexOf(item) >=0 )
console.log("交集:",result);

执行结果:

例:求差集

const arr1 = [22,33,55,33,11,5,6];
const arr2 = [22,55,77,5,88,99];
//方法一
const s1 = new Set(arr1);
const s2 = new Set(arr2);
const result = new Set([...s1,...s2].filter(item => !s2.has(item) || !s1.has(item)));
console.log("差集:",result);
//方法二
const s1 = new Set(arr1);
const s2 = new Set(arr2);
const result = [...s1,...s2].filter(item =>arr1.indexOf(item) >=0 && arr2.indexOf(item) <0 || arr2.indexOf(item) >=0 && arr1.indexOf(item)<0)
console.log("差集:",result);

执行结果:

5、手写set方法

我们手写的set方法跟浏览器提供的set对比的话肯定是不一样的,因为浏览器在实现ES标准的时候,它是可以调用底层资源的,比如说可以直接操作内存,它的效率要比我们手写的set方法效率高些。手写源码可以拓展我们的思维,仅此而已。

1、新建mySet.js文件

class MySet {
    //构造函数里面参数可传可不传,给参数一个默认值就可以了
    constructor(iterable = []) {
        //验证传入的参数是否是可迭代对象,可迭代的对象类型一定是个对象
        if (typeof iterable[Symbol.iterator] !== 'function') {
            throw new TypeError(`${typeof iterator} ${iterator} is not iterable (cannot read property Symbol(Symbol.iterator))`)
        }
        this._datas = []; //由于不能操作底层内存,所以声明一个数组用于存放数据
        //下面验证通过,迭代每一个可迭代的对象,把每一项放到  MySet里面去
        for (const item of iterator) {
            //每迭代一次,把item加到 MySet里面去,问题转为封装add方法
            this.add(item);
        }
    }
    //给 MySet添加数据
    add(data) {
        //这里加数据的时候有个前提条件,就是重复的数据只放一次,问题转为封装has方法
        if (!this.has(data)) {
            this._datas.push(data);
        }
    }
    //判断 MySet中是否存在对应的数据
    has(data) {
        //这里判断是否有相同的值,问题转为封装isEqual方法
        for (const item of this._datas) {
            if (this.isEqual(data, item)) {
                return true;
            }
        }
        return false;
    }
    //删除 MySet中对应的数据
    delete(data) {
        for (let i = 0; i < this._datas.length; i++) {
            const element = this._datas[i];
            if (this.isEqual(element, data)) {
                //删除
                this._datas.splice(i, 1);
                return true;
            }
        }
        return false;
    }
    //清空整个数组
    clear() {
        this._datas.length = 0;
    }
    *[Symbol.iterator]() {
        for (const item of this._datas) {
            yield item;
        }
    }
    forEach(callback) {
        for (const item of this._datas) {
            callback(item, item, this);
        }
    }
    /**
     * 判断两个数据是否相等
     * @param {*} data1 
     * @param {*} data2 
     */
    isEqual(data1, data2) {
        //如果data1和data2都等于0,那么我们认为它们相等
        if (data1 === 0 && data2 === 0) {
            return true;
        }
        return Object.is(data1, data2);
    }
}

代码测试:求差集

`
     const arr1 = [22,33,55,33,11,5,6];
        const arr2 = [22,55,77,5,88,99];
        const s1 = new MySet(arr1);
        const s2 = new MySet(arr2);
        const result = new MySet([...s1,...s2].filter(item => !s2.has(item) || !s1.has(item)));
        console.log("差集:",result); `

执行结果

代码测试:add、has、delete、clear


 const result = new MySet();
       result.add(1);
       result.add(2);
       result.add(3);
       result.add(4);
       console.log(result);

温泉里挣扎的set()方法_第7张图片

以上就是我的分享,希望能帮到大家!欢迎大佬一起讨论......

你可能感兴趣的:(前端,javascript,es6)