Skip to content

Set

一直以来,JS 只能使用数据和对象来保存多个数据,缺乏其他语言那样拥有丰富的集合类型。因此,在 ES6 新增了两种集合类型(Set 和 Map),用于在不同的场景中发挥作用。

Set 用于存放不重复的数据

1. 如何创建 set 集合

javascript
// 创建了一个空的set集合
let mySet = new Set()
javascript
// 创建了一个具有初始内容的set集合,内容来自于可迭代对象每一次迭代的结果
let mySet = new Set(iterable)

2. 如何对 set 集合进行操作

  • add: 添加一个数据到 set 集合的末尾,如果数据已存在,则不进行任何操作。
    • set 使用Object.is的方式判断数据是否相同
    • 特殊情况:针对+0 和-0 的情况,是相同的。
  • has: 判断 set 中是否存在对应的数据
  • delete: 从 set 集合中删除对应的数据,如果数据不存在,则不进行任何操作。
  • clear: 清空 set 集合
  • size: 获取 set 集合的长度

3. 如何与数组进行相互转换

javascript
const s = new Set([1, 2, 3, 4, 5])
// set 本身也是一个可迭代对象,每次迭代的结果就是每一项的值
const arr = [...s]

去重

javascript
const arr = [22, 11, 33, 42, 21, 43, 55, 33]
const result = [...new Set(arr)]
console.log(result) // [22, 11, 33, 42, 21, 43, 55]

const str = 'aassqweerqqffoopp'
const s = new Set(str).join('')
console.log(s) // ['a', 's', 'q', 'w', 'e', 'r', 'o', 'p']

4. 如果遍历

4.1 for...of 遍历

4.2 set 方法携带的 forEach 方法遍历

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

javascript
const s = new Set([1, 2, 3, 4, 5])
s.forEach((item, item2, s) => {
  console.log(item, item, s)
})

5. 应用

求两个数组的并集、交集、差集

javascript
const arr1 = [1, 3, 4, 5, 5, 9, 9]
const arr2 = [1, 2, 4, 5, 6, 7, 8]

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

// 交集
const intersection = new Set(arr1.filter((x) => arr2.includes(x)))
console.log(intersection) // Set(3) {1, 4, 5}

// 差集
const difference = [...new Set([...arr1, ...arr2])].filter(
  (x) =>
    (arr1.includes(x) && !arr2.includes(x)) ||
    (!arr1.includes(x) && arr2.includes(x))
)

console.log(difference) // Set(6) {3, 9}

6. 手写 Set

模拟 Set 的实现

javascript
class MySet {
  constructor(iterator) {
    // 验证是否是可迭代对象
    if (typeof iterator[Symbol.iterator] !== 'function') {
      throw new TypeError(`你提供的${iterator}不是一个可迭代的对象`)
    }

    this._datas = []
    for (let item of iterator) {
      this.add(item)
    }
  }

  add(data) {
    if (!this.has(data)) {
      this._datas.push(data)
    }
  }
  has(data) {
    for (let item of this._datas) {
      if (this.isEqual(data, item)) {
        return true
      }
      return false
    }
  }
  delete(data) {
    for (let i = 0; i < this._datas.length; i++) {
      const element = this._datas[i]
      if (this.isEqual(data, element)) {
        this._datas.splice(i, 1)
        return true
      }
    }
    return false
  }
  clear() {
    this._datas.length = 0
  }
  forEach(callback) {
    for (let item of this._datas) {
      callback(item, item, this)
    }
  }
  // 将该对象转变成可以迭代的对象
  *[Symbol.iterator]() {
    for (let item of this._datas) {
      yield item
    }
  }
  isEqual(data1, data2) {
    if (data1 === 0 && data2 === 0) {
      return true
    }
    return Object.is(data1, data2)
  }
}