函数式编程中通用去重工具的核心是不修改原数据、接受任意可迭代对象、支持自定义唯一性逻辑、返回新数组;基础版用 set + 展开实现原始值去重,高阶版 uniqueby 支持键提取,uniquewith 支持自定义相等逻辑。

在函数式编程中,通用去重工具的核心是:**不修改原数据、接受任意可迭代对象、支持自定义唯一性逻辑、返回新数组**。关键不是“怎么写一个去重函数”,而是“怎么设计一个可组合、可复用、无副作用的纯函数”。
用 Set + map 实现基础通用去重
最简洁通用的方式是结合 Set 去重原始值(如字符串、数字),再通过 map 保持映射能力:
const unique = (iterable) => [...new Set(iterable)];
这个版本适用于:
• 数组、类数组、字符串、Set 等可展开对象
• 元素为原始值(=== 可比较)
• 保持首次出现顺序
例如:unique([1, 2, 2, 3, 1]) // [1, 2, 3]unique('abac') // ['a', 'b', 'a', 'c'] → ['a', 'b', 'c']
支持自定义键提取(keyBy)的高阶去重
当处理对象数组时,需指定“依据哪个字段判断唯一”。推荐用高阶函数接收一个 key extractor 函数:
const uniqueBy = (getKey) => (iterable) => {
const seen = new Set();
return Array.from(iterable).filter(item => {
const key = getKey(item);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
};
使用方式自然且可组合:
-
uniqueBy(x => x.id)([{id: 1}, {id: 2}, {id: 1}])→ 去重后保留第一个{id: 1} -
uniqueBy(x => x.name.toLowerCase())(['Alice', 'bob', 'ALICE'])→ 忽略大小写去重 - 可配合其他函数:
pipe(users, filter(active), uniqueBy('id'))(若你有 pipe 工具)
兼容深比较或复杂唯一逻辑(谨慎使用)
如果必须按对象内容(而非引用或某个字段)去重,应明确这是性能敏感操作,不建议默认启用。可单独封装为可选行为:
const uniqueWith = (equals) => (iterable) => {
const arr = Array.from(iterable);
return arr.filter((item, i) =>
arr.slice(0, i).every(prev => !equals(prev, item))
);
};
// 示例:按结构相等去重(浅比较)
const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
uniqueWith(isEqual)([{x:1}, {y:2}, {x:1}]); // [{x:1}, {y:2}]
⚠️ 注意:
• JSON.stringify 不适合含函数、undefined、Symbol、循环引用的数据
• 真正的深比较建议用 lodash.isEqual 或自研轻量版,但要清楚代价
小技巧:让工具真正“函数式”
确保它符合函数式习惯:
• 只依赖输入参数:不读取外部变量、不调用 Date.now() 等非纯操作
• 返回新数组:绝不修改传入的数组或对象
• 类型友好:若用 TypeScript,可标注泛型:<t>(iterable: Iterable<t>): T[]</t></t>
• 可部分应用:像 uniqueBy(prop('id')) 比 uniqueBy('id') 更函数式(避免字符串魔法)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











