ES6 Set数据结构实战:5个你没想到的去重场景(含对象去重方案)

ES6 Set数据结构实战:5个你没想到的去重场景(含对象去重方案)

如果你以为Set只是用来给数组去重的,那可能错过了它一半以上的威力。在日常开发中,我们常常会陷入一种“工具只用其最基础功能”的思维定式。Set数据结构,这个ES6引入的“集合”概念,其核心价值在于唯一性高效的成员存在性检查。这两个特性,一旦跳出简单的数字、字符串去重,就能在前端性能优化、状态管理、甚至服务端数据处理中,解决一些令人头疼的“重复”问题。这篇文章,我们就来聊聊那些教科书里不常讲,但实战中极其好用的Set应用场景,特别是如何巧妙地处理复杂对象去重,并提供可直接复制粘贴的代码方案。

1. 超越基础:复杂数据结构的去重 Hack

数组去重是Set的入门课,[...new Set(array)]几乎成了条件反射。但当我们面对对象数组时,这个魔法就失效了。因为Set判断唯一性是基于SameValueZero算法(类似于===,但认为NaN === NaN),两个内容相同的对象引用不同,Set会视为两个不同的元素。

那么,对象数组如何去重? 直接使用Set行不通,我们需要一个“转换器”,将对象映射为Set可以识别的唯一键。

1.1 基于特定属性组合的去重

最常见的场景是根据对象的某个或某几个属性进行去重。例如,从接口返回的用户列表中,根据用户ID去重。

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 1, name: 'Alice' }, // 重复ID
  { id: 3, name: 'Charlie' }
];

// 方法一:使用 Map 辅助,保留首次出现的对象
const uniqueUsersById = Array.from(
  new Map(users.map(user => [user.id, user])).values()
);
console.log(uniqueUsersById);
// 输出: [{id:1, name:'Alice'}, {id:2, name:'Bob'}, {id:3, name:'Charlie'}]

注意:Map构造函数接受一个可迭代的键值对数组。[user.id, user]id作为键,对象本身作为值。当键重复时,后面的值会覆盖前面的,所以我们用.values()取回对象,并利用Map键唯一性实现了去重。

1.2 基于对象完整内容的去重

如果需要根据整个对象的结构化内容去重(即所有属性值都相同),JSON.stringify是一个常用但需要谨慎使用的方案。

const items = [
  { a: 1, b: 2 },
  { a: 1, b: 2 }, // 完全重复
  { a: 2, b: 3 },
  { b: 2, a: 1 } // 属性顺序不同,但内容相同
];

// 方法二:使用 JSON.stringify + Set
// 注意:此方法对属性顺序敏感,且无法处理包含 undefined、函数、循环引用的对象
const seen = new Set();
const uniqueItems = items.filter(item => {
  const key = JSON.stringify(item);
  if (seen.has(key)) {
    return false;
  }
  seen.add(key);
  return true;
});
console.log(uniqueItems);
// 输出: [{a:1, b:2}, {a:2, b:3}]
// 注意:{b:2, a:1} 因为字符串化后顺序不同,会被误判为不同对象!

为了解决属性顺序问题,我们可以创建一个规范化键的函数:

function getStableObjectKey(obj) {
  // 递归地对对象进行排序后字符串化(简化版,仅适用于可序列化对象)
  const normalized = Object.keys(obj)
    .sort()
    .reduce((acc, key) => {
      acc[key] = obj[key];
      return acc;
    }, {});
  return JSON.stringify(normalized);
}

// 使用稳定键进行去重
const stableSeen = new Set();
const trulyUniqueItems = items.filter(item => {
  const key = getStableObjectKey(item);
  if (stableSeen.has(key)) return false;
  stableSeen.add(key);
  return true;
});
console.log(trulyUniqueItems);
// 输出: [{a:1, b:2}, {a:2, b:3}] 成功将顺序不同的相同对象去重

对于更复杂、不可序列化的对象(如包含函数、DOM元素),上述方法失效。此时,可以考虑使用WeakMap结合自定义哈希函数,或者直接使用Map存储对象引用本身,但这通常意味着你的数据设计可能需要重新审视。

2. 前端交互:表单重复提交与操作防抖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值