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存储对象引用本身,但这通常意味着你的数据设计可能需要重新审视。

&spm=1001.2101.3001.5002&articleId=159031017&d=1&t=3&u=e753faf0156e4c63ae51925c65d7951f)
621

被折叠的 条评论
为什么被折叠?



