Skip to content

数组去重的五种方式

方法一:Set + 展开运算符

Set 只允许存储唯一值,是去重的首选方案。一行代码即可完成,且对于基本类型值有非常好的可读性。

js
const list = [8, 1, 1, 2, 3, 4, 5, 7, 7, 6];
const uniqueList = [...new Set(list)];
// [8, 1, 2, 3, 4, 5, 7, 6]

时间复杂度 O(n)。但 Set 基于 SameValueZero 算法比较:NaN 会被去重(同一 NaN 只保留一个),而 {} !== {} 所以对象不会被去重——实际上对象数组中每个对象的引用不同,Set 不会视其为重复。

方法二:filter + indexOf

利用 filter 遍历原数组,只保留第一次出现的元素——indexOf 返回的索引等于当前索引时说明该元素是首次出现。

js
const list = [8, 1, 1, 2, 3, 4, 5, 7, 7, 6];
const uniqueList = list.filter((item, index) => {
  return list.indexOf(item) === index;
});

时间复杂度 O(n²),因为 indexOf 本身是 O(n) 的遍历。对于几千项以内的数组通常可接受,但数据量更大时应优先使用 Set 方案。

方法三:reduce 累积

reduce 逐个检查当前元素是否已在累积数组中,只在不存在时追加。

js
const list = [8, 1, 1, 2, 3, 4, 5, 7, 7, 6];
const uniqueList = list.reduce((accumulator, current) => {
  if (!accumulator.includes(current)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);

时间复杂度 O(n²),优势在于逻辑清晰且有扩展空间——在 includes 处可以替换为自定义比较逻辑。

方法四:forEach + indexOf

逐一遍历,只在目标数组中不存在当前值时推入。

js
const list = [1, 2, 2, 3, 4, 4, 5];
const uniqueList = [];
list.forEach((value) => {
  if (uniqueList.indexOf(value) === -1) {
    uniqueList.push(value);
  }
});

方法五:forEach + includes

与方法四逻辑相同,将 indexOf === -1 替换为 !includes(),语义更直观。

js
const list = [1, 2, 2, 3, 4, 4, 5];
const uniqueList = [];
list.forEach((value) => {
  if (!uniqueList.includes(value)) {
    uniqueList.push(value);
  }
});

方法对比

方法时间复杂度代码简洁度适用场景
Set + 展开O(n)⭐⭐⭐⭐⭐基本类型数组,首选方案
filter + indexOfO(n²)⭐⭐⭐⭐中小型数组,无兼容性问题
reduceO(n²)⭐⭐⭐需要在去重逻辑中插入自定义步骤
forEach + indexOf/includesO(n²)⭐⭐⭐追求明确步骤控制的场景

对于面试场景,熟悉多种实现方式并理解各自的复杂度特征是核心考察点;对于生产代码,直接用 Set 即可。