JS数据去重的7种实用方法总结(附完整代码与原理)

 更新时间:2025年11月17日 09:16:17   作者:前端开发小透明  
在JavaScript中数据去重是一个常见的操作,特别是在处理数组时,这篇文章主要介绍了JS数据去重的7种实用方法,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在 JavaScript 开发中,“数据去重” 是高频需求 —— 比如处理接口返回的重复列表、用户输入的重复值等。不同场景下,适合的去重方法不同,有的追求简洁,有的追求性能,有的需要兼容复杂数据类型。今天就整理 7 种常用的去重方法,每种都附带代码和核心原理,帮你轻松应对各种去重场景。

一、基础方法:利用 Set 去重(最简洁)

核心原理:ES6 新增的 Set 集合本身具有 “值唯一” 的特性,能自动忽略重复值,再通过扩展运算符(...)或 Array.from() 转回数组即可。

代码实现

// 待去重数组(包含数字、字符串、重复值)
const arr = [1, 2, 2, "3", "3", 4, 4, 5];
// 方法 1:扩展运算符转数组
const uniqueArr1 = [...new Set(arr)];
// 方法 2:Array.from() 转数组(兼容不支持扩展运算符的环境)
const uniqueArr2 = Array.from(new Set(arr));
console.log(uniqueArr1); // 输出:[1, 2, "3", 4, 5]
console.log(uniqueArr2); // 输出:[1, 2, "3", 4, 5]

优缺点

  • 优点:代码最简洁,一行搞定;性能优秀(底层是哈希表实现,时间复杂度 O (n));

  • 缺点:无法区分 “值相同但类型不同” 的数据(比如 1"1" 会被视为不同值,这其实是合理的,若需强制相同需额外处理);不支持对象 / 数组等引用类型的去重(引用不同会被视为不同值)。

适用场景

处理简单数据类型(数字、字符串、布尔值)的数组,追求代码简洁和性能。

二、传统方法:利用 indexOf/includes 去重

核心原理:创建一个新数组,遍历原数组,判断当前元素是否在新数组中(用 indexOfincludes),不在则加入新数组,最终得到去重后的数组。

代码实现(indexOf 版)

const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
 // indexOf 返回 -1 表示元素不在新数组中
 if (uniqueArr.indexOf(arr[i]) === -1) {
   uniqueArr.push(arr[i]);
 }
}
console.log(uniqueArr); // 输出:[1, 2, 3]

代码实现(includes 版,更直观)

const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [];
for (let item of arr) {
 // includes 直接返回布尔值,判断元素是否存在
 if (!uniqueArr.includes(item)) {
   uniqueArr.push(item);
 }
}
console.log(uniqueArr); // 输出:[1, 2, 3]

优缺点

  • 优点:兼容性好(支持 ES5 及以上,无需担心环境问题);逻辑直观,容易理解;

  • 缺点:性能较差(indexOf/includes 每次都会遍历新数组,时间复杂度 O (n²),数据量大时会卡顿);同样不支持引用类型去重。

适用场景

处理小规模的简单类型数组,或需要兼容低版本浏览器的场景。

三、优化性能:利用对象键名唯一性去重

核心原理:对象的键名(key)具有唯一性(不能重复),遍历原数组时,把元素作为对象的键名存储,同时判断键名是否已存在,不存在则加入新数组。

代码实现

const arr = [1, 2, 2, "2", 3, 3];
const uniqueArr = [];
const tempObj = {}; // 临时对象,用于存储已存在的元素
for (let item of arr) {
 // 把元素转为字符串作为键名(避免 1 和 "1" 被视为不同键)
 const key = typeof item + item;
 if (!tempObj[key]) {
   tempObj[key] = true; // 标记为已存在
   uniqueArr.push(item);
 }
}
console.log(uniqueArr); // 输出:[1, 2, "2", 3]

关键细节

  • 为什么要加 typeof item?比如 1(数字)和 "1"(字符串),直接用 item 当键名会都是 "1",导致误判;加 typeof 后,键名会变成 "number1""string1",能正确区分。

  • 若想强制把 1"1" 视为相同值,可去掉 typeof,直接用 const key = item

优缺点

  • 优点:性能优秀(对象的键名查询是哈希表操作,时间复杂度 O (n));支持区分 “值相同类型不同” 的场景;

  • 缺点:需额外处理键名类型(避免误判);不支持引用类型去重(对象作为键名会被转为 [object Object],导致所有对象都被视为相同)。

适用场景

处理大规模的简单类型数组,追求高性能,且需要区分值类型。

四、进阶方法:利用 filter + indexOf 去重

核心原理filter 方法会筛选出满足条件的元素,结合 indexOf 判断 “当前元素在原数组中的第一次出现位置是否等于当前索引”—— 如果等于,说明是第一次出现,保留;否则是重复值,过滤掉。

代码实现

const arr = [1, 2, 2, 3, 3, 4];
// filter 回调函数:返回 true 则保留元素
const uniqueArr = arr.filter((item, index) => {
 // indexOf 会返回元素在数组中第一次出现的索引
 return arr.indexOf(item) === index;
});
console.log(uniqueArr); // 输出:[1, 2, 3, 4]

优缺点

  • 优点:代码简洁(一行搞定);逻辑优雅,适合函数式编程风格;

  • 缺点:性能差(filter 遍历一次,indexOf 每次又遍历一次,时间复杂度 O (n²));不支持引用类型去重。

适用场景

处理小规模简单类型数组,追求函数式编程风格,不关心极致性能。

五、ES6 进阶:利用 Map 去重

核心原理Map 的键(key)可以是任意类型,且具有唯一性。遍历原数组时,用 Map.has() 判断元素是否已存在,不存在则用 Map.set() 存储,并加入新数组。

代码实现

const arr = [1, 2, 2, "3", "3", 4];
const uniqueArr = [];
const tempMap = new Map();
for (let item of arr) {
 if (!tempMap.has(item)) { // 判断元素是否已在 Map 中
   tempMap.set(item, true); // 存储元素(值随便设,只要有标记即可)
   uniqueArr.push(item);
 }
}
console.log(uniqueArr); // 输出:[1, 2, "3", 4]

优缺点

  • 优点:性能优秀(时间复杂度 O (n));支持更多键类型(比如 NaNSet 也支持,但 indexOf 不支持 NaN 的判断);

  • 缺点:代码比 Set 稍繁琐;不支持引用类型去重(引用不同会被视为不同键)。

适用场景

处理简单类型数组,或需要存储 NaN 等特殊值的场景(indexOf 无法判断 NaN,但 Map.has(NaN) 可以)。

六、复杂场景:引用类型(对象 / 数组)去重

前面的方法都无法处理对象 / 数组等引用类型(比如 { id: 1 }{ id: 1 } 会被视为不同值,因为引用不同)。此时需要通过 “比较内容” 来实现去重,常用方法是 JSON.stringify 转字符串手动比较属性

方法 1:JSON.stringify 转字符串(简单对象适用)

核心原理:把对象转为 JSON 字符串,再用 Set 或对象键名去重(字符串可以被唯一识别)。

// 待去重的对象数组(id 相同视为重复)
const arr = [
 { id: 1, name: "小明" },
 { id: 1, name: "小明" }, // 重复
 { id: 2, name: "小红" }
];
const uniqueArr = [...new Set(arr.map(item => JSON.stringify(item)))]
 .map(str => JSON.parse(str)); // 转回对象
console.log(uniqueArr);
// 输出:[{ id: 1, name: "小明" }, { id: 2, name: "小红" }]

方法 2:手动比较属性(复杂对象适用)

核心原理:指定一个唯一标识(比如 id),遍历数组时,判断新数组中是否已有该标识的对象,没有则加入。

const arr = [
 { id: 1, name: "小明" },
 { id: 1, name: "小明" },
 { id: 2, name: "小红" }
];
const uniqueArr = [];
for (let item of arr) {
 // 用 some 判断新数组中是否已有相同 id 的对象
 const isRepeat = uniqueArr.some(uniqueItem => uniqueItem.id === item.id);
 if (!isRepeat) {
   uniqueArr.push(item);
 }
}
console.log(uniqueArr);
// 输出:[{ id: 1, name: "小明" }, { id: 2, name: "小红" }]

优缺点

  • 优点:能处理引用类型的去重;

  • 缺点JSON.stringify 有局限性(比如无法处理 functionundefinedSymbol 等属性);手动比较属性需指定唯一标识,灵活性较低。

适用场景

处理对象数组,且对象结构简单(无特殊属性),或有明确唯一标识(如 id)的场景。

七、排序后去重:利用 sort + 相邻比较

核心原理:先通过 sort() 把数组排序(相同元素会相邻),再遍历排序后的数组,比较当前元素和前一个元素,不同则加入新数组。

代码实现

const arr = [3, 1, 2, 2, 3, 1, 4];
const uniqueArr = [];
// 先排序(注意:sort 默认按字符串排序,数字需加比较函数)
const sortedArr = arr.sort((a, b) => a - b); // 排序后:[1, 1, 2, 2, 3, 3, 4]
for (let i = 0; i < sortedArr.length; i++) {
 // 比较当前元素和前一个元素,不同则保留
 if (sortedArr[i] !== sortedArr[i - 1]) {
   uniqueArr.push(sortedArr[i]);
 }
}
console.log(uniqueArr); // 输出:[1, 2, 3, 4]

优缺点

  • 优点:逻辑简单,适合已排序或可排序的数组;

  • 缺点:会改变原数组的顺序(若需保留原顺序则不适用);性能受排序影响(sort 时间复杂度约 O (n log n));不支持引用类型去重。

适用场景

处理数字数组,且不关心原数组顺序的场景。

总结:不同场景如何选?

场景需求推荐方法时间复杂度
简单类型 + 代码简洁Set 去重O(n)
简单类型 + 高性能 + 大规模对象键名去重 / Map 去重O(n)
低版本浏览器兼容indexOf/includes 去重O(n²)
函数式编程风格filter + indexOf 去重O(n²)
对象数组(有唯一标识)手动比较属性去重O(n²)
数字数组(不关心顺序)sort + 相邻比较去重O(n log n)

记住:没有 “最好” 的去重方法,只有 “最适合” 的 —— 根据数据类型、数据规模和环境需求选择即可!

到此这篇关于JS数据去重的7种实用方法总结的文章就介绍到这了,更多相关JS数据去重方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 禁用Enter键表单自动提交实现代码

    禁用Enter键表单自动提交实现代码

    这篇文章主要介绍了禁用Enter键表单自动提交实现代码,需要的朋友可以参考下
    2014-05-05
  • javascript showModalDialog,open取得父窗口的方法

    javascript showModalDialog,open取得父窗口的方法

    showModalDialog,open取得父窗口的代码,需要的朋友可以参考下。
    2010-03-03
  • 通过Javascript读取本地Excel文件内容的代码示例

    通过Javascript读取本地Excel文件内容的代码示例

    这篇文章主要介绍了通过Javascript读取本地Excel文件内容的代码示例,但需要一定的条件才可以使用js操作本地文件,需要的朋友参考下吧
    2014-04-04
  • JS取文本框中最小值的简单实例

    JS取文本框中最小值的简单实例

    这篇文章主要介绍了JS取文本框中最小值的简单实例,有需要的朋友可以参考一下
    2013-11-11
  • JavaScript数组去重由慢到快由繁到简(优化篇)

    JavaScript数组去重由慢到快由繁到简(优化篇)

    本文给大家介绍通过indexof去重,hash去重,排序后去重及set去重由慢到快有繁到简的方法给大家介绍了js数组去重的方法,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-08-08
  • js的math中缺少的数学方法小结

    js的math中缺少的数学方法小结

    JavaScript Math对象包含一些真正有用且强大的数学运算,但它缺乏大多数其他语言提供的许多重要运算,例如求和,乘积,奇数和偶数等等,本文就来介绍一下
    2023-08-08
  • javascript实现炫酷的拖动分页

    javascript实现炫酷的拖动分页

    非常酷的javascript拖动分页功能,无缝循环分页,拖动鼠标即可完成分页,鼠标向左拖动回到前一页,向右拖动则翻开第二页,还带有动画特效,着实很不错,界面黑色,非主流风格,相信很多人会喜欢的。
    2015-05-05
  • GoJs中使用HTML方法示例

    GoJs中使用HTML方法示例

    这篇文章主要为大家介绍了GoJs中使用HTML方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • JS Web Flex弹性盒子模型代码实例

    JS Web Flex弹性盒子模型代码实例

    这篇文章主要介绍了JS Web Flex弹性盒子模型代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • js判断一个元素是否为另一个元素的子元素的代码

    js判断一个元素是否为另一个元素的子元素的代码

    用js判断一个元素是否为另一个元素的子元素,再做一些效果的时候经常用到,特别是和鼠标事件相关的应用中,比如一个浮层,在鼠标操作浮层内元素的时候浮层显示,当点击浮层外的元素的时候隐藏浮层
    2012-03-03

最新评论