javascript中对象数组去重不能用set或filter+indexof,需按内容深比较(如json.stringify或lodash isequal)或按唯一字段(如id)去重,避免引用比较失效。

JavaScript 中对包含引用类型对象的数组去重,不能直接用 Set 或 filter + indexOf,因为对象比较是按引用而非值。关键在于定义“重复”的标准:是全等(属性名+值完全一致),还是仅按某个唯一字段(如 id)判重?下面分场景给出实用方案。
按对象内容深比较去重(全等判定)
适用于结构相对固定、需严格比对所有字段的场景。推荐使用 JSON.stringify 作为临时哈希键(注意:仅限可序列化字段,且顺序敏感):
const arr = [{a: 1, b: 2}, {b: 2, a: 1}, {a: 1, b: 2}];
const unique = Array.from(
new Map(arr.map(item => [JSON.stringify(item), item])).values()
);
// → [{a: 1, b: 2}, {b: 2, a: 1}]
更健壮的方式是用 Lodash 的 uniqWith 配合 isEqual:
- 安装:
npm install lodash - 使用:
import { uniqWith, isEqual } from 'lodash'; const unique = uniqWith(arr, isEqual); - 优点:支持函数、undefined、循环引用(Lodash v4.17.21+)、属性顺序无关
按指定字段(如 id)去重(最常用)
多数业务中,对象有唯一标识(如 id、name),按该字段去重高效且语义清晰:
const arr = [{id: 1, name: 'A'}, {id: 2, name: 'B'}, {id: 1, name: 'A-2'}];
const seen = new Set();
const unique = arr.filter(item => {
if (seen.has(item.id)) return false;
seen.add(item.id);
return true;
});
// → [{id: 1, name: 'A'}, {id: 2, name: 'B'}]
一行写法(适合简单场景):
const unique = arr.filter((item, i, self) => i === self.findIndex(obj => obj.id === item.id) );
处理嵌套对象或需自定义逻辑
当“唯一性”依赖多字段组合或深层属性时,手动构造 key 更可控:
- 用模板字符串生成 key:
`${item.user.id}-${item.timestamp}` - 用数组拼接再转字符串:
JSON.stringify([item.category, item.status]) - 避免直接用
toString()—— 所有对象默认返回[object Object]
不推荐的做法及坑点
以下方式在引用类型数组中基本无效:
-
[...new Set(arr)]:每个对象都是新引用,Set 认为全不重复 -
arr.filter((v, i) => arr.indexOf(v) === i):indexOf内部用===比较引用,永远返回true -
JSON.stringify直接用于含函数、Date、undefined、NaN 的对象:会丢失或转为 null
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











