温泉里挣扎的set()方法

AlgoZenith
• 阅读 2379

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

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

0、如何创建 set 集合

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

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

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

执行结果:
温泉里挣扎的set()方法

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

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

执行结果:
温泉里挣扎的set()方法

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

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

执行结果:
温泉里挣扎的set()方法

如果是字符串,它会先转换为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里面它是怎么判断两个数据是否相同呢? 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);

执行结果:
温泉里挣扎的set()方法

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

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

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

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

执行结果:
温泉里挣扎的set()方法

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

执行结果:
温泉里挣扎的set()方法

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

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

执行结果:
温泉里挣扎的set()方法

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

执行结果:
温泉里挣扎的set()方法

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

顺便写下字符串去重

例:字符串去重

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

执行结果:
温泉里挣扎的set()方法

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()方法

  • 使用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()方法

使用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()方法

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

注意: 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);

执行结果:
温泉里挣扎的set()方法

例:求交集

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

执行结果:
温泉里挣扎的set()方法

例:求差集

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

执行结果:
温泉里挣扎的set()方法

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

代码测试:求差集

`<script src="js/mySet.js"></script>
    <script > 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); </script>`

执行结果温泉里挣扎的set()方法

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

<script src="js/mySet.js"></script>
<script > const result = new MySet();
       result.add(1);
       result.add(2);
       result.add(3);
       result.add(4);
       console.log(result);</script>

温泉里挣扎的set()方法

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

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
typeScript数据类型
//布尔类型letisDone:booleanfalse;//数字类型所有数字都是浮点数numberletdecLiteral:number6;lethexLiteral:number0xf00d;letbinaryLiteral:number0b101
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这