ES6 之 Set 解析

174 阅读6分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」。

一、概述

Set 是 ES6 所提供的新的数据结构。它类似于数组,但是它的成员值都是唯一的,没有重复的值

二、应用

1、基本用法

Set 本身是一个构造函数,用来生成 Set 数据结构。下面我们来看具体代码:

var arr1 = [0, 1, 2, 2, 3, 4, 5, 5, 6, 6]
const s = new Set()
arr1.forEach(item => {
   s.add(item)
})

for (const i of s) {
   console.log(i)
}

1)首先定义一个有重复数据的数组 arr1;

2)new 一个 Set 实例对象 s;

3)遍历 arr1,将 arr1 中的值依次插入 s 中,如果 s 中有已存在的数据,则不重复插入;

4)最后遍历 s,并打印其成员值,结果如下图所示:

由此印证了那句话:“Set 成员值都是唯一的,没有重复的值!”。

同时我们也知道了一点,Set 可用于数据去重。具体操作如下:

2、普通数组去重

对于普通数组,可直接使用 Set 进行去重,除了 “基本用法” 中成员是 Number 类型的数组去重以外,还可以对成员是 String 类型的数组去重,代码如下:

var arr1 = ['0', '1', '2', '3', '3', '4', '5', '5']

const s = new Set()
arr1.forEach(item => {
   s.add(item)
})

for (const i of s) {
   console.log(i)
}

输出结果如下图所示:

3、复杂数组去重

对于复杂数组,比如对象数组,没办法直接去重,代码如下:

var arr1 = [{
   name: '海绵宝宝',
   age: 5
}, {
   name: '派大星',
   age: 10
}, {
   name: '派大星',
   age: 10
}, {
   name: '海绵宝宝',
   age: 5
}, {
   name: '蟹老板',
   age: 15
}]
const s = new Set()
arr1.forEach(item => {
   s.add(item)
})

for (const i of s) {
   console.log(i)
}

输出结果如下图所示(没有去重):

要想去重,我们可以将代码修改成如下形式:

var arr1 = [{
   name: '海绵宝宝',
   age: 5
}, {
   name: '派大星',
   age: 10
}, {
   name: '派大星',
   age: 10
}, {
   name: '海绵宝宝',
   age: 5
}, {
   name: '蟹老板',
   age: 15
}]
const s = new Set()
arr1.forEach(item => {
   s.add(JSON.stringify(item))
})

const newArr = Array.from(s)
for (const i of s) {
   console.log(JSON.parse(i))
}

输出结果如下图所示:

由以上代码可以看出:

1)往 s 中插入成员的时候,我们是先将 item 转为 JSON 字符串,否则无法去重,这也说明 Set 也可用于字符串字符去重;

2)我们通过 Array.from() 将 s 进行转型,这么做的原因是,我们前面提到 Set 只是类似于数组,并不是真正的数组,所以我们需要将其转为数组。对比如下所示:

console.log(s)

输出结果如下图所示:

console.log(newArr)

输出结果如下图所示:

3)除了使用 Array.from() 将 s 进行转型以外,我们还可以通过"扩展运算符..." 将 s 转换成数组,代码如下:

const newArr = [...s]

输出结果一致,如图所示:

4、字符串去重

通过 Set 可以将字符串中重复的字符去除,代码如下:

var stringValue = 'aabcdee'
const newStringValue = [...new Set(stringValue)].join('')
console.log(newStringValue)

输出结果如图所示:

5、注意

由以上例子,我们可以发现一个现象,那就是 Set 加入值的时候,其中的成员值不会发生类型转换,原来的值是 Number 类型,那么加入 Set 以后,仍然是 Number 类型。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为 NaN 等于自身,而精确相等运算符认为 NaN不 等于自身。具体代码如下:

const s = new Set()
const a = NaN
const b = NaN
s.add(a)
s.add(b)
console.log(s)

输出结果如下图所示:

由此可以看出,虽然 Set 实例添加了两次 NaN,但是只会加入一个。这表明,在 Set 内部,两个 NaN 是相等的。

除此以外,两个对象总是不相等的,代码如下:

const s = new Set()
s.add({})
console.log(s.size)// 1

s.add({})
console.log(s.size)// 2

输出结果如下图所示:

由此可见,两个空对象被认为是不相等的,所以才会有两个值。

6、Set 实例的属性和方法

执行以下代码:

const s = new Set()
console.log(s)

输出结果如下图所示:

由上图可以看出,Set 有以下几个属性

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。

1)size

const s = new Set()
console.log(s.size)// 0

Set 共有2类方法,其中有用于操作数据的操作方法,如下:

  • Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为 Set 的成员。
  • Set.prototype.clear():清除所有成员,没有返回值。

1)add()

s.add('11111')
s.add('22222')
console.log(s)

输出结果如下图所示:

2)has()

console.log(s.has('11111'))// true
console.log(s.has('22222'))// true
console.log(s.has('33333'))// false

输出结果如下图所示:

3)delete()

s.delete('11111')
console.log(s)
console.log(s.has('11111'))// false

输出结果如下图所示:

4)clear()

s.clear()
console.log(s)

输出结果如下图所示:

用于遍历成员的遍历方法:

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

注意:需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

遍历操作的具体代码如下:

1)keys()

const arr1 = ['cat', 'dog', 'pig']
const s = new Set(arr1)

for (const item of s.keys()) {
   console.log(item)
}

输出结果如下图所示:

2)values()

for (const item of s.values()) {
   console.log(item)
}

输出结果如下图所示:

3)entries()

for (const item of s.entries()) {
   console.log(item)
}

输出结果如下图所示:

4)forEach()

s.forEach((value, key) => {
   console.log(key + ':' + value)
})

输出结果如下图所示:

7、Set 实现并集、交集和差集

Set 不仅可以用于数据去重,它还可以很容易地实现并集、交集和差集,具体实现方式如下:

1)并集

const a = new Set([1, 2, 3, 4, 5])
const b = new Set([1, 3, 5, 7, 9])

// 并集
const union = new Set([...a, ...b])
console.log(union)// Set {1, 2, 3, 4, 5, 7, 9}

输出结果如下图所示:

2)交集

// 交集
const intersect = new Set([...a].filter(x => b.has(x)))
console.log(intersect)// set {1, 3, 5}

输出结果如下图所示:

3)差集

// (a 相对于 b 的)差集
const difference = new Set([...a].filter(x => !b.has(x)))
console.log(difference)// Set {2, 4}

输出结果如下图所示:

// (b 相对于 a 的)差集
const difference1 = new Set([...b].filter(x => !a.has(x)))
console.log(difference1)// Set {7, 9}

输出结果如下图所示:

三、参考

1.(总结)js中操作数组的小技巧

2.(总结)object对象的常用操作

3.(总结)一些字符串操作的小技能