JS 数据结构 —— 集合 🔥

269 阅读4分钟

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

ES6 中,出现了一种新的数据结构 Set。数组的去重,利用 Set 可以非常快速的实现,比如有个 numbers 数组 [1, 2, 3, 3, 4, 5] ,只需要执行 [...new Set(numbers)] 就可以实现去重的效果。

image.png

这借助了 Set 中的元素是不能重复的这一特点。在 MDN 的介绍里,有这么一句话:

Set 对象是值的集合

那么问题来了,什么是集合?本篇文章就打算简绍下集合的概念,并基于对象封装一个集合结构。

介绍

集合通常是由无序且不重复的元素构成。不重复这点字面理解即可,无序则意味着不能像数组那样通过下标值访问集合中的元素。集合一般的实现方式是哈希表,但下面我们先以对象为基础来实现一个简单的集合类 Set

封装

属性

Set 类中定义一个属性 items,初始值是个空对象,之后添加到集合中的元素都存放到 items 对象中。注意, Objectkeys 本身,就是一个集合。

class Set {
  constructor() {
    this.items = {}
  }
  // 下面添加方法
}

方法

has()

先来实现用于查询元素是否存在于集合中的 has() 方法。该方法传入一个参数 value,用于判断添加的元素 value 是否已经存在。

// has(value),查询元素是否存在
has(value) {
  return this.items.hasOwnProperty(value)
}

由于 items 是个对象,所以可以用 hasOwnProperty() 方法来查看 items 中是否已经存在 value 并返回布尔值。

add()

add() 方法传入一个 value 作为参数,用于向集合增添元素。之所以前面先实现了 has() 方法,就是因为在向集合添加元素前,需要判断集合中是否已经存在该元素,避免重复。如果原本集合中没有,那么直接以该元素作为 items 对象的一个属性的键(key)。

// add(value),添加元素 value
add(value) {
  if (this.has(value)) return false
  this.items[value] = value
  return this.items
}

显然,这里只是做了个简单的实现,以理解大概的原理。不考虑传入的 value 是对象或是数组,或是字符串类型的数字之类的情况。

clear()

clear() 方法用于清空集合,只需要将 items 重置为空对象即可

// clear(),清空集合
clear() {
  this.items = {}
}

delete()

delete()方法用于删除集合中与传入的 value 相等的元素。删除前最好先判断一下该元素在集合中是否存在。

// delete(value),删除元素
delete(value) {
  if (this.has(value)) {
    delete this.items[value]
    return true
  } else {
    return false
  }
}

values()

values() 方法用于获取集合中所有元素,返回结果是一个数组。由于是借助 Object.keys() 实现的,所以得到的元素都必须是可枚举的。

// values(),获取集合中的所有元素
values() {
  return Object.keys(this.items)
}

size()

size() 方法用于获取集合中元素的个数。只需要在执行 .values 后去获取得到的数组的 length 即可。

// size(),获取集合中元素的个数
size() {
  return this.values().length
}

集合间的操作

高中数学里我们都学过关于集合的交集并集等知识,这些操作也同样适用于我们封装的 Set 类。下面,就是对集合之间操作的封装,这些方法依旧是定义在 Set 类中的。

并集 union()

如果一个集合等于将集合 A 和集合 B 的所有元素加在一起,我们称之为集合 A 与集合 B 的并集,如下图所示:

image.png

union() 方法传入一个要合并的集合作为参数,然后与实例本身进行并集操作。思路就是先新建一个空集合 newSet,通过上面实现的 values() 方法拿到实例本身(this)也就是集合 A 的所有元素,然后进行 for 循环遍历,将每个元素加到新集合中。对集合 B 也是做同样的处理,只不过下面的代码里我用了 reduce() 取代 for 循环。因为在 add() 方法中已经对加入的元素是否重复进行了判断,所以新集合中的每个元素都是不一样的。

// union(set),将传入的集合 set 与实例本身做并集操作
union(set) {
  const newSet = new Set()
  const valuesA = this.values()
  for (let i = 0; i < valuesA.length; i++) {
    newSet.add(valuesA[i])
  }
  set.values().reduce((pre, cur) => {
    pre.add(cur)
    return pre
  }, newSet)
  return newSet
}

交集

交集就是集合 A 和集合 B 中共有的元素组成的集合,如下图所示:

image.png

代码的思路也很简单,新建一个集合,然后通过 this.values() 获取当前实例本身(也就是集合 A)的元素组成的数组,再对数组做个遍历,如果数组中的元素存在与集合 B 中(通过前面实现的 has() 方法),则添加到新集合中:

// intersection(set),将传入的集合 set 与实例本身进行交集操作
intersection(set) {
  const newSet = new Set()
  this.values().reduce((pre, cur) => {
    if (set.has(cur)) pre.add(cur)
    return pre
  }, newSet)
  return newSet
}

感谢.gif 点赞.png