纯函数去重需满足不可变、无副作用、相同输入输出相同:值类型用filter+indexOf或Set;对象按字段去重用uniqueBy;深比较用uniqueByDeep配合纯equals函数。

在不可变数据流中实现纯函数去重,核心是:不修改原数组、不依赖外部状态、相同输入永远返回相同输出。关键在于用 filter + indexOf 或 Set 构建无副作用的映射逻辑,同时兼容对象等引用类型时需额外处理。
基础数组去重(值类型)
对数字、字符串等原始类型,可直接利用索引判定首次出现位置:
const unique = arr => arr.filter((item, index) => arr.indexOf(item) === index); // 示例 unique([1, 2, 2, 3, 1]); // [1, 2, 3]
该函数满足纯函数要求:无外部依赖、不改变输入、结果仅由输入决定。
使用 Set 提升性能(仍限值类型)
当数组较大时,indexOf 的 O(n²) 开销明显。Set 可降为 O(n),且保持不可变性:
立即学习“Java免费学习笔记(深入)”;
const unique = arr => {
const seen = new Set();
return arr.filter(item => {
if (seen.has(item)) return false;
seen.add(item);
return true;
});
};
- 注意:
Set内部仍属局部变量,未逃逸到函数外,不破坏纯度 - 原始类型(如
1、"a")比较准确;但{}、[]会被视为不同引用,无法去重
对象数组按字段去重(纯函数方案)
对对象,需指定唯一标识字段(如 id),用 Map 或累积对象记录已见 key:
const uniqueBy = (arr, keyFn) => {
const seen = new Set();
return arr.filter(item => {
const key = keyFn(item);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
};
// 按 id 去重
uniqueBy(
[{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 1, name: 'c'}],
x => x.id
); // [{id: 1, name: 'a'}, {id: 2, name: 'b'}]
-
keyFn确保逻辑可复用、可测试 - 仍保持纯:所有状态(
seen)严格限定在函数作用域内
深层对象结构去重(谨慎使用)
若需基于整个对象内容去重(如深比较),纯函数下应避免运行时序列化(如 JSON.stringify),因其不支持函数、undefined、循环引用。更稳妥的方式是显式定义比较逻辑:
const uniqueByDeep = (arr, equals) => {
return arr.filter((item, i) =>
i === arr.findIndex(other => equals(item, other))
);
};
// 使用示例:按 name 和 age 判定相等
uniqueByDeep(
[{name: 'Alice', age: 30}, {name: 'Bob', age: 25}, {name: 'Alice', age: 30}],
(a, b) => a.name === b.name && a.age === b.age
);
-
equals函数必须自身为纯函数 - 性能较低(O(n²)),适合小数据量或对一致性要求极高的场景


















