96SEO 2026-05-08 19:32 0
在前端开发的日常琐碎中,处理数组数据几乎是家常便饭。而在这其中,数组去重geng是像空气一样无处不在。你可Neng觉得这事儿太简单了一行 不就搞定了吗?嘿,别急,要是真这么简单,面试官怎么总爱拿这个问倒一大片候选人?

今天咱们不搞虚的,深入聊聊 TypeScript 里数组去重的那些事儿。我们将从Zui朴素的循环,一路聊到函数式编程,再到处理复杂对象的黑科技。这里整理了 20 种不同的实现思路,有些是工程实战的利器,有些则是用来锻炼脑细胞的学术派招数。不管你是刚入行的小白,还是久经沙场的老手,这篇文章douNeng让你对“去重”这件事有全新的认识。
第一部分:原始暴力的“蛮力派”解法你依然得会写。
1. 双重循环索引比对这是Zui符合直觉的思路:拿每一个元素,去跟它前面的所有元素比一比。Ru果前面没出现过那它就是“独苗”,赶紧收进结果集。
function uniqueByDoubleLoop: T {
const result: T = ;
for {
for {
if {
// Ru果索引相等,说明前面没找到重复的,这是第一次见
if result.push;
break; // 只要找到重复或者确认唯一,就跳出内层循环
}
}
}
return result;
}
这种写法逻辑简单,但时间复杂度是 O。数据量一大,浏览器主线程就得卡死,慎用。
2. 新数组 + includes 检查比双重循环稍微现代一点,利用了数组原生的 includes 方法。代码kan起来清爽多了但骨子里还是 O 的复杂度,因为 includes 本身也是一次遍历查找。
function uniqueByIncludes: T {
const result: T = ;
for {
// 每次dou要遍历 result 数组,效率并不高
if ) {
result.push;
}
}
return result;
}
3. 原地 splice
不想开辟新数组?那就原地修改。为了防止删除元素导致索引错位,我们通常采用倒序遍历的策略。这种方法虽然省了点内存,但 splice 操作本身会引起数组元素的移动,性Neng开销依然hen大。
function uniqueBySpliceReverse: T {
let len = arr.length;
while {
// 倒序检查,Ru果前面有重复的,就把当前的删掉
for {
if {
arr.splice;
break;
}
}
}
return arr;
}
4. 原地 splice
Ru果你非要从前往后遍历,也不是不行,但逻辑会稍微绕一点:遇到重复的,删除后面的那个,并且要把索引回退一步,否则会漏掉下一个元素的检查。
function uniqueBySpliceForward: T {
let len = arr.length;
for {
for {
if {
arr.splice;
j--; // 索引回退,因为后面的元素前移了
len--; // 长度也要减
}
}
}
return arr;
}
5. forEach + indexOf
这是一种比较经典的函数式写法雏形。利用 indexOf 总是返回元素第一次出现的位置的特性,Ru果当前索引和第一次出现的索引一致,那就是唯一的。
function uniqueByForEach: T {
const result: T = ;
arr.forEach => {
if === index) {
result.push;
}
});
return result;
}
6. 双重 while 倒序 splice
这其实是方法3的变种,把 for 换成了 while,显得geng极客一点,但原理完全一致。
function uniqueByWhile: T {
let len = arr.length;
while {
let i = len;
while {
if {
arr.splice;
break;
}
}
}
return arr;
}
第二部分:函数式编程的“优雅派”解法
随着 ES6 的普及,我们开始追求代码的声明式和简洁性。虽然有些写法Ru果不注意底层原理,性Neng依然是个坑,但在数据量不大的业务逻辑里它们确实Neng极大地提升代码可读性。
7. filter + indexOf 一行流这绝对是面试中出现率Zui高的“金句”。短短一行代码,既展示了去重逻辑,又展示了函数式编程的魅力。不过别忘了filter 内部每次dou要调用 indexOf,性Neng依然是 O。
const uniqueByFilterIndex = : T =>
arr.filter => arr.indexOf === i);
8. filter + Set 闭包
这才是函数式编程的正确打开方式!利用 Set 的 O 查找特性,配合闭包缓存Yi存在的元素,既简洁又高效。注意这里用了 !! 来强制转换布尔值,因为 Set.add 返回的是 Set 对象本身。
const uniqueByFilterSet = : T => {
const seen = new Set;
return arr.filter && !!seen.add);
};
9. reduce 累加器
reduce 是数组方法里的瑞士军刀。我们Ke以用它来逐步构建结果数组。不过这里Ru果用 includes 判断,还是逃不掉 O 的命运。记得在 TypeScript 里要显式标注泛型 T,否则类型推断可Neng会抽风。
const uniqueByReduce = : T =>
arr.reduce => {
if ) acc.push;
return acc;
}, );
10. reduce + Set 闭包
把 Set 引入到 reduce 中,就完美解决了性Neng问题。这是兼顾优雅与速度的典范。
const uniqueByReduceSet = : T => {
const seen = new Set;
return arr.reduce => {
if ) {
seen.add;
acc.push;
}
return acc;
}, );
};
11. Object 键值对哈希
在 Map 普及之前,我们常用普通对象来模拟哈希表。利用对象键的唯一性来实现去重。这里有个坑:1 和 '1' 在对象键里可Neng会冲突,所以我们要用 typeof 拼接前缀来区分。TypeScript 的泛型约束 T extends string | number | boolean Neng在编译期就帮你拦住对象类型,避免运行时出错。
function uniqueByObjectKey: T {
const obj: Record = {};
return arr.filter(item => {
const key = typeof item + String;
if ) {
return false;
}
obj = true;
return true;
});
}
第三部分:现代工程首选的“高效派”解法
到了现代前端开发,性Neng和类型安全是硬指标。ES6 引入的 Set 和 Map 彻底改变了游戏规则。
没有比这geng简洁的了。利用
运算符 ... 把 Set 转回数组。这是 90% 场景下的Zui佳实践。它基于 SameValueZero 算法,连 NaN douNeng正确处理。
const uniqueBySet = : T => ;
13. Map 键值映射
Ru果你需要在去重的同时保留一些元数据,或者仅仅是喜欢 Map 的 API,Ke以用这个方法。把元素本身同时当作键和值。
function uniqueByMap: T {
const map = new Map;
arr.forEach);
return ;
}
14. Object 字面量
强调对象大法,但这次我们用 Object.values 来取值。要注意,数字键会被自动排序,所以顺序可Neng会乱。而且 1 和 '1' 依然是个雷区。
function uniqueByObjValues: T {
const obj = {} as Record;
for {
obj = item;
}
return Object.values;
}
第四部分:特殊场景的“特种部队”
有时候,简单的去重满足不了需求。比如你需要去重后的数据是有序的,或者你需要处理复杂的对象数组。
15. sort + spliceRu果你不介意改变数组顺序,甚至希望结果是有序的,那先排序是个好主意。排序后相同的元素就会挨在一起,处理起来就简单多了。注意 sort 默认是按字符串排的,数字要传比较函数。
function uniqueBySortSplice: number {
arr.sort => a - b);
let len = arr.length;
while {
if {
arr.splice;
}
}
return arr;
}
16. sort + filter 相邻判重
思路同上,只是用 filter geng函数式一点。只保留当前元素不等于前一个元素的情况。
const uniqueBySortFilter = : number => {
arr.sort => a - b);
return arr.filter => i === 0 || item !== arr);
};
17. 双指针
这是算法题里的经典解法。排序后用慢指针 slow 记录不重复元素的边界,快指针 fast 去探索。遇到不重复的,就把 slow 往前挪一位并赋值。这是原地修改数组且空间复杂度 O 的Zui优解。
function uniqueByTwoPointers: number {
if return arr;
arr.sort => a - b);
let slow = 0;
for {
if {
arr = arr;
}
}
return arr.slice;
}
18. 递归原地删除
递归虽然在实际工程中因为栈溢出风险用得少,但在理解算法逻辑时hen有帮助。这里我们每次处理数组的Zui后一个元素,kan它在前面的部分有没有出现过有就删掉,然后递归处理长度减一的数组。
function uniqueRecursive: T {
const len = length ?? arr.length;
if return arr;
const last = len - 1;
for {
if {
arr.splice;
break;
}
}
return uniqueRecursive;
}
19. 递归拼接返回
这次我们不修改原数组,而是通过递归把不重复的元素一层层拼回去。这种纯函数式的写法kan着hen舒服,但性Neng开销也不小。
function uniqueRecursivePure: T {
const len = length ?? arr.length;
if return arr.slice;
const lastItem = arr;
let isRepeat = false;
for {
if {
isRepeat = true;
break;
}
}
const head = uniqueRecursivePure;
return isRepeat ? head : head.concat;
}
20. JSON 字符串化
这是处理对象数组的常见“偏方”。因为对象是引用类型,{id:1} !== {id:1}。我们Ke以把它们转成 JSON 字符串,用字符串去重,再转回来。注意:这招对键顺序敏感,且无法处理函数、
function uniqueByJson: T {
const seen = new Set;
const result: T = ;
for {
const key = JSON.stringify;
if ) {
seen.add;
result.push;
}
}
return result;
}
与实战建议
kan了这么多,脑子是不是有点乱?别担心,实际开发中你只需要记住几个核心原则:
首选 Set对于基本类型, 永远是你的第一选择。代码短、性Neng好、语义清晰。
对象数组用 MapRu果是 User 这种对象数组,想按 id 去重,用 Map 是Zui稳的。
const users = ;
const uniqueUsers = ;
// 结果:
慎用 splice 和双重循环除非数据量极小,否则这些 O 的操作dou是性Neng杀手。
利用 TypeScript 泛型写工具函数时善用 和类型约束,让编译器帮你挡住低级错误。比如 uniqueByObjectKey 限制只Neng传基本类型,就避免了运行时把对象转成 的尴尬。
Zui后去重虽然是个小功Neng,但背后折射出的是对数据结构、时间复杂度以及语言特性的理解深度。希望这 20 种方法Neng成为你武器库里的趁手兵器,下次遇到“去重”需求时Neng信手拈来游刃有余!
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback