一维数组与对象数组转Set去重差异原因及问题排查
为什么一维数组和对象数组用Set去重表现不同?
这个问题的核心在于Set判断元素唯一性的规则,以及JavaScript中基本类型和引用类型的本质区别,咱们一步步拆解:
一、差异的根本原因
Set内部判断两个值是否重复,用的是严格相等运算符(===),这是关键:
- 对于基本类型(数字、字符串、布尔值等):只要值相同,
===就返回true。比如你示例里的arr1,两个2是完全相同的值,Set会自动剔除重复项,所以去重成功。 - 对于引用类型(对象、数组等):哪怕两个对象的内容完全一模一样,它们也是内存中独立的引用。比如你数组里第一个
{description: "d1", ...}和第三个,看起来内容相同,但obj1 === obj2会返回false,Set会把它们当成不同的元素,自然不会去重。
你的操作误区就在于:忽略了引用类型的特性,直接把对象数组套用了基本类型的去重方法,导致Set无法识别出内容重复的对象。
二、对象数组正确的去重方法
既然要基于对象的内容去重,我们需要自定义“重复”的规则(比如根据id或item_id这种唯一标识),下面给几种常用方案:
方案1:利用filter + findIndex
通过判断当前元素的唯一标识在数组中第一次出现的索引是否等于当前索引,来保留唯一元素:
let arr = [{description: "d1", item_id: 298237, id: 298237, show_tag: 0, tags: "6,7,15,16,20"}, {description: "d2", item_id: 297743, id: 297743, show_tag: 0, tags: "5,7,15,16,20"}, {description: "d1", item_id: 298237, id: 298237, show_tag: 0, tags: "6,7,15,16,20"}, {description: "d2", item_id: 297743, id: 297743, show_tag: 0, tags: "5,7,15,16,20"} ]; let uniqueArr = arr.filter((item, index, self) => { // 只保留当前id第一次出现的元素 return self.findIndex(i => i.id === item.id) === index; }); console.log(uniqueArr); // 输出两个唯一的对象
方案2:利用Map存储已出现的标识(性能更优)
对于大数据量的数组,findIndex会重复遍历,用Map可以实现O(n)的时间复杂度:
const idMap = new Map(); let uniqueArr = []; for (const item of arr) { // 如果Map中没有当前id,就添加进去并保留该元素 if (!idMap.has(item.id)) { idMap.set(item.id, item); uniqueArr.push(item); } } console.log(uniqueArr);
方案3:序列化对象(不推荐,有局限性)
如果没有明确的唯一标识,也可以把对象序列化为字符串再用Set去重,但要注意:对象的属性顺序会影响序列化结果,且如果对象包含函数、undefined等,序列化会丢失信息,仅适合简单的纯数据对象:
let uniqueArr = Array.from( new Set(arr.map(item => JSON.stringify(item))) ).map(str => JSON.parse(str)); console.log(uniqueArr);
总结
- Set的去重逻辑是严格相等:基本类型靠值判断,引用类型靠内存引用判断,这是两者表现不同的核心原因。
- 对象数组去重不能直接用Set,必须根据对象内容的唯一标识(或序列化)自定义去重规则。
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

