JS `Set`:唯一值集合,用于去重与集合操作

各位观众老爷们,大家好!今天咱们聊聊JavaScript里一个挺低调但贼好使的家伙——Set。别看它名字简单,用途可大了去了,尤其是在去重和各种集合操作方面,简直是神器级别的存在。

Set是个啥玩意儿?

简单来说,Set就是JavaScript里用来存储唯一值的集合。注意!是唯一值!也就是说,你往里头放重复的东西,它只会保留一份。这特性用来去重简直不要太爽。

你可以把它想象成一个不允许有双胞胎的俱乐部,谁要是想偷偷溜进来一个长得一模一样的,立马被踢出去。

怎么用Set?

创建Set非常简单:

const mySet = new Set();

现在 mySet 就是一个空的集合了,等着你往里头塞东西。

往Set里添加元素

add() 方法往 Set 里添加元素:

mySet.add(1);
mySet.add(2);
mySet.add(3);
mySet.add(1); // 再次添加1,会被忽略

console.log(mySet); // 输出: Set(3) { 1, 2, 3 }

看到了吧?即使我们添加了两次 1Set 里也只会保留一个。这就是唯一性的威力。

检查Set里有没有某个元素

has() 方法来检查 Set 里有没有某个元素,返回 truefalse

console.log(mySet.has(2)); // 输出: true
console.log(mySet.has(4)); // 输出: false

从Set里删除元素

delete() 方法来删除 Set 里的元素:

mySet.delete(2);
console.log(mySet); // 输出: Set(2) { 1, 3 }

Set的大小

size 属性来获取 Set 里有多少个元素:

console.log(mySet.size); // 输出: 2

清空Set

clear() 方法来清空 Set 里的所有元素:

mySet.clear();
console.log(mySet.size); // 输出: 0

Set的迭代

Set 是可迭代的,也就是说你可以用 for...of 循环来遍历它的元素:

const mySet = new Set([1, 2, 3, 4, 5]);

for (const item of mySet) {
  console.log(item);
}
// 输出:
// 1
// 2
// 3
// 4
// 5

你也可以使用 forEach() 方法:

mySet.forEach(item => {
  console.log(item);
});
// 输出:
// 1
// 2
// 3
// 4
// 5

Set的用武之地:去重

这绝对是 Set 最常用的场景之一。 给定一个数组,里面有很多重复的元素,怎么快速地把它去重呢?

const myArray = [1, 2, 2, 3, 4, 4, 5, 5, 5];
const uniqueArray = [...new Set(myArray)]; // 使用扩展运算符将Set转换回数组

console.log(uniqueArray); // 输出: [ 1, 2, 3, 4, 5 ]

是不是很简单粗暴? 先用 new Set(myArray) 创建一个 Set,它会自动把重复的元素去掉。 然后用扩展运算符 ...Set 转换回数组。搞定!

Set的进阶用法:集合操作

Set 不仅仅能去重,还能进行各种集合操作,比如并集、交集、差集等等。

  • 并集 (Union)

    并集是指两个集合的所有元素合并在一起,重复的元素只保留一个。

    function union(setA, setB) {
      const unionSet = new Set(setA); // 先复制一份 setA
      for (const elem of setB) {
        unionSet.add(elem); // 把 setB 里的元素添加到 unionSet 里
      }
      return unionSet;
    }
    
    const setA = new Set([1, 2, 3]);
    const setB = new Set([3, 4, 5]);
    
    const unionSet = union(setA, setB);
    console.log(unionSet); // 输出: Set(5) { 1, 2, 3, 4, 5 }
  • 交集 (Intersection)

    交集是指两个集合共有的元素。

    function intersection(setA, setB) {
      const intersectionSet = new Set();
      for (const elem of setB) {
        if (setA.has(elem)) {
          intersectionSet.add(elem); // 如果 setA 里也有这个元素,就添加到 intersectionSet 里
        }
      }
      return intersectionSet;
    }
    
    const setA = new Set([1, 2, 3]);
    const setB = new Set([3, 4, 5]);
    
    const intersectionSet = intersection(setA, setB);
    console.log(intersectionSet); // 输出: Set(1) { 3 }
  • 差集 (Difference)

    差集是指存在于一个集合,但不存在于另一个集合的元素。

    function difference(setA, setB) {
      const differenceSet = new Set(setA); // 先复制一份 setA
      for (const elem of setB) {
        differenceSet.delete(elem); // 把 setA 里和 setB 相同的元素删掉
      }
      return differenceSet;
    }
    
    const setA = new Set([1, 2, 3]);
    const setB = new Set([3, 4, 5]);
    
    const differenceSet = difference(setA, setB);
    console.log(differenceSet); // 输出: Set(2) { 1, 2 }  (A相对于B的差集)
  • 对称差集 (Symmetric Difference)

    对称差集是指只存在于其中一个集合,但不同时存在于两个集合的元素。 简单来说,就是并集减去交集。

    function symmetricDifference(setA, setB) {
        const unionSet = new Set(setA);
        for(const elem of setB){
            unionSet.add(elem);
        }
    
        const intersectionSet = new Set();
        for (const elem of setB) {
          if (setA.has(elem)) {
            intersectionSet.add(elem);
          }
        }
    
        const symmetricDifferenceSet = new Set(unionSet);
        for(const elem of intersectionSet){
            symmetricDifferenceSet.delete(elem);
        }
        return symmetricDifferenceSet;
    }
    
    const setA = new Set([1, 2, 3]);
    const setB = new Set([3, 4, 5]);
    
    const symmetricDifferenceSet = symmetricDifference(setA, setB);
    console.log(symmetricDifferenceSet); // 输出: Set(4) { 1, 2, 4, 5 }
  • 子集 (Subset)

    判断一个集合是否是另一个集合的子集。

    function isSubset(setA, setB) {
      if (setA.size > setB.size) {
        return false; // 如果 setA 比 setB 大,肯定不是子集
      }
      for (const elem of setA) {
        if (!setB.has(elem)) {
          return false; // 如果 setA 里有任何一个元素不在 setB 里,就不是子集
        }
      }
      return true;
    }
    
    const setA = new Set([1, 2]);
    const setB = new Set([1, 2, 3]);
    
    console.log(isSubset(setA, setB)); // 输出: true
    console.log(isSubset(setB, setA)); // 输出: false
  • 超集 (Superset)

    判断一个集合是否是另一个集合的超集(包含另一个集合的所有元素)。

    function isSuperset(setA, setB) {
      if (setA.size < setB.size) {
        return false; // 如果 setA 比 setB 小,肯定不是超集
      }
      for (const elem of setB) {
        if (!setA.has(elem)) {
          return false; // 如果 setB 里有任何一个元素不在 setA 里,就不是超集
        }
      }
      return true;
    }
    
    const setA = new Set([1, 2, 3]);
    const setB = new Set([1, 2]);
    
    console.log(isSuperset(setA, setB)); // 输出: true
    console.log(isSuperset(setB, setA)); // 输出: false

Set的注意事项

  • Set 里的元素是无序的。虽然你添加元素的顺序会影响迭代的顺序,但 Set 本身并不保证元素的顺序。
  • Set 只能存储唯一值。如果你需要存储重复的值,请使用数组或其他数据结构。
  • Set 可以存储任何类型的值,包括原始类型(如数字、字符串、布尔值)和对象。但是,对于对象来说,Set 判断唯一性的标准是对象的引用是否相同,而不是对象的内容是否相同。

    const obj1 = { name: 'Alice' };
    const obj2 = { name: 'Alice' };
    const mySet = new Set();
    mySet.add(obj1);
    mySet.add(obj2); // obj1 和 obj2 是不同的对象,所以都会被添加到 Set 里
    
    console.log(mySet.size); // 输出: 2

Set和其他数据结构的对比

特性 Set Array Object Map
存储 唯一值集合 有序的值列表 键值对集合 键值对集合
唯一性 保证元素唯一 不保证元素唯一 键唯一,值可以重复 键唯一,值可以重复
顺序 无序 (迭代顺序取决于插入顺序) 有序 无序 (ES6 之后迭代顺序取决于插入顺序) 有序 (迭代顺序取决于插入顺序)
查找元素 has() 方法, O(1) 平均时间复杂度 indexOf(), includes(), O(n) 时间复杂度 hasOwnProperty(), O(1) 平均时间复杂度 has(), O(1) 平均时间复杂度
添加元素 add(), O(1) 平均时间复杂度 push(), unshift(), O(1) 或 O(n) obj[key] = value, O(1) 平均时间复杂度 set(), O(1) 平均时间复杂度
删除元素 delete(), O(1) 平均时间复杂度 pop(), shift(), splice(), O(n) delete obj[key], O(1) 平均时间复杂度 delete(), O(1) 平均时间复杂度
迭代 可迭代 可迭代 ES6 之前不可直接迭代,ES6 之后可迭代键 可迭代
用途 去重, 集合操作 存储有序数据, 遍历 存储键值对, 对象属性 存储键值对, 更灵活的键类型 (可以是对象)

Set的实际应用场景

  • 用户权限管理:可以用 Set 来存储用户的角色或权限,方便进行权限判断。
  • 跟踪已访问的URL:防止重复请求相同的URL。
  • 缓存:可以用 Set 来记录哪些数据已经被缓存,避免重复计算。
  • 数据分析:统计不同用户的行为,去除重复的行为记录。
  • 算法题:很多算法题都可以用 Set 来简化代码,比如判断一个数组里是否有重复的元素。

Set的高级技巧

  • 使用Set进行字符串去重

    const str = "abracadabra";
    const uniqueStr = [...new Set(str)].join('');
    
    console.log(uniqueStr); // 输出: "abrcd"
  • 使用Set进行DOM元素去重

    const elements = document.querySelectorAll('.my-element'); // 获取所有class为my-element的元素
    const uniqueElements = [...new Set(elements)]; // 去重
    
    console.log(uniqueElements.length); // 输出去重后的元素数量
  • 配合其他数据结构使用

    Set 可以和其他数据结构(如数组、对象、Map)配合使用,解决更复杂的问题。 比如说,你可以用 Map 来存储每个元素出现的次数,然后用 Set 来存储只出现一次的元素。

总结

Set 是 JavaScript 里一个非常实用且高效的数据结构。 它最大的特点就是存储唯一值,可以用来去重和进行各种集合操作。 掌握 Set 的用法可以让你写出更简洁、更高效的代码。 希望今天的讲解对大家有所帮助!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注