在 JavaScript 开发中,数组去重是一个常见且基础的需求,无论是处理用户输入的数据,还是从后端获取的数据集,我们经常需要对数组中的重复元素进行清理。本文将详细介绍 JavaScript 中常见的数组去重方法,并分析它们的性能和适用情况,帮助你在实际开发中做出更合适的选择。
数组去重的应用场景
数据清洗
- 在实际开发中,数据来源复杂多样,包括用户输入、API接口返回以及数据库查询结果等,这些数据中常存在冗余值。通过数组去重操作,可有效清除重复项,从而提升后续数据处理的准确性与效率。
- 例如,在处理用户提交的表单数据时,网络异常或用户误操作可能导致数据重复录入,此时去重机制对保障数据完整性至关重要。
前端展示优化
- 前端开发过程中,若页面展示的列表、下拉选项等内容存在重复数据,将导致界面混乱并影响用户体验。通过数组去重,可确保展示内容均为唯一值,使页面呈现简洁且结构清晰。
- 以城市选择下拉菜单为例,若原始数据中存在重复的城市名称,去重后菜单选项将更加清晰明了,便于用户快速定位目标选项。
统计分析需求
- 在数据统计与分析场景中,核心目标常聚焦于唯一值的数量及分布特征。数组去重能够快速提取唯一元素集合,为分析提供精准数据基础。
- 例如,统计网站的独立访客数量时,用户可能在一天内多次访问并生成重复记录,此时需基于用户ID组成的数组进行去重处理,方能计算实际独立访客数量。
业务功能实现
- 诸多业务逻辑的实现依赖数组去重技术。
- 在表单提交场景中,通过去重机制可有效避免重复数据提交,保障业务逻辑的严谨性。
典型应用场景示例
- 搜索关键词去重
在搜索框的自动补全功能中,对历史关键词数组进行去重处理,可避免向用户展示冗余建议,帮助用户高效定位目标信息。 - 用户行为记录分析
统计用户单日访问的页面URL时,通过对URL数组去重,可精准掌握用户的浏览路径与兴趣分布,支撑行为分析。 - 数据可视化
在生成可视化图表时,对原始数据数组进行去重,可确保图表仅反映唯一数据点的分布规律,避免因重复值导致结果失真。 - 多数据源整合
合并多个来源的数据时,不同数据源常存在重复记录。通过数组去重,可将多个列表整合为唯一元素集合,便于后续统一处理与分析。
一、基础方法篇
1. 双重 for 循环(暴力去重)
function uniqueByLoop(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j-- // 删除元素后调整索引
}
}
}
return arr
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByLoop(arr));
原理:外层循环遍历数组中的每个元素,内层循环从外层循环当前元素的下一个位置开始,将后续元素与当前元素进行比较。如果发现相同元素,则使用 splice 方法将其从数组中删除,并调整内层循环的索引。
特点:
- 时间复杂度高:时间复杂度为 O(n²) ,当数据量较大时,性能会显著下降。
- 修改原数组:该方法会直接修改原数组,可能会产生副作用,使用时需要注意。
- 兼容性好:可以在所有 JavaScript 环境中使用。
2. forEach + indexOf
function uniqueByIndexOf(arr) {
const result = []
arr.forEach(item => {
result.indexOf(item) === -1 && result.push(item)
})
return result
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByIndexOf(arr));
- 原理:利用
forEach方法遍历数组,对于每个元素,使用indexOf方法检查它在结果数组中是否已经存在。如果不存在,则将其添加到结果数组中。 - 优化点:不修改原数组,而是返回一个新的去重后的数组。
- 局限:无法正确识别
NaN,因为indexOf(NaN)始终返回-1。
二、ES5 进阶方案
3. filter 过滤法
function uniqueByFilter(arr) {
return arr.filter((item, index) =>
arr.indexOf(item) === index
)
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByFilter(arr));
- 优点:使用函数式编程的方式,代码简洁易读。
- 缺点:同样存在无法识别
NaN的问题。 - 性能:比
forEach + indexOf略优,但时间复杂度仍为 O(n²) 。
4. 对象键值唯一性
function uniqueByObjectKey(arr) {
const obj = {}
arr.forEach(item => obj[item] = true)
return Object.keys(obj).map(k =>
isNaN(k) ? NaN : Number(k)
)
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByObjectKey(arr));
注意事项:
- 类型转换:所有元素会被转换为字符串类型,需要手动处理
Number和NaN类型。 - 重复判断问题:数字
1和字符串'1'会被视为重复元素。
三、ES6+ 现代方案
5. Set 数据结构(推荐首选)
const uniqueBySet = arr => [...new Set(arr)];
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueBySet(arr));
优势:
- 性能最优:时间复杂度为 O(n) ,在处理大规模数组时性能表现出色。
- 代码简洁:只需一行代码即可实现去重。
- 正确识别 NaN:
Set中NaN被视为等于自身。
6. Map 数据结构
function uniqueByMap(arr) {
const map = new Map()
return arr.filter(item =>
!map.has(item) && map.set(item, true)
)
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByMap(arr));
特性:
- 保留插入顺序:可以保留元素的插入顺序。
- 处理对象引用类型:能够处理对象引用类型的去重,这是基础方法无法实现的。
- 性能接近 Set:性能与
Set方案接近。
四、特殊场景处理
7. 排序后相邻去重
function uniqueBySort(arr) {
return arr.concat().sort()
.filter((item, index, array) =>
!index || item !== array[index - 1]
)
}
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueBySort(arr));
适用场景:适用于允许改变元素顺序的情况。
注意事项:
- 排序问题:对字符串和数字混合的数组进行排序时,可能会产生意外的结果。
- 深拷贝:如果需要深拷贝数组,可以使用
[...arr].sort()。
8. reduce 累积器
reduce是 JavaScript 数组的一个高阶方法,用于对数组中的每个元素执行一个提供的回调函数,并将其结果汇总为单个值。它接收两个参数:- 回调函数:用于处理数组中的每个元素,该回调函数接收四个参数,这里使用了其中两个:
acc(accumulator):累加器,它是上一次回调函数执行的返回值,初始值由reduce方法的第二个参数指定。cur(currentValue):当前正在处理的数组元素。
- 初始值:
reduce方法的第二个参数,这里是一个空数组[],表示累加器acc的初始值。
- 回调函数:用于处理数组中的每个元素,该回调函数接收四个参数,这里使用了其中两个:
const uniqueByReduce = arr =>
arr.reduce((acc, cur) =>
acc.includes(cur) ? acc : [...acc, cur],
[]
)
let arr = [1, 2, 4, 5, 5, 2, 1, 1, 4, 6];
console.log(uniqueByReduce(arr));
- 优势:是函数式编程的典范,代码简洁且具有良好的扩展性。
- 扩展性:方便添加复杂的去重逻辑。
总结
JavaScript 数组去重的方法多种多样,每种方法都有其独特的优缺点和适用场景。在实际开发中,我们需要根据具体需求进行选择:
- 如果追求代码简洁和高性能,且项目运行在支持 ES6 的环境中,推荐使用
Set数据结构进行去重。 - 如果需要兼容不支持 ES6 的旧环境,可以考虑使用
filter + indexOf等兼容性较好的方法。 - 如果需要处理对象引用类型或保留元素的插入顺序,可以选择
Map数据结构。 - 如果对元素顺序没有要求,且数组元素类型较为单一,可以使用排序后相邻去重的方法。
希望通过本文的介绍,你能对 JavaScript 数组去重有更深入的理解,在实际开发中能够灵活运用各种方法,提高代码的质量和性能。