JS 数组去重全方案:从基础到通用万能去重函数

JS 数组去重全方案:从基础到通用万能去重函数

日常开发中数组去重是高频需求,场景却各不相同:普通基础类型数组、单字段对象数组、多字段联合去重、自定义规则去重。网上方案零散,很多只适配单一场景。本文循序渐进梳理所有主流去重写法,最后封装一个支持多字段、自定义比较函数 的通用万能去重工具 unique3,覆盖 99% 业务场景。

一、基础数组去重(纯数字 / 字符串等基本类型)

仅针对 Number/String/Boolean/null/undefined 这类基础值数组,不支持对象、数组引用类型。

1. Set 一行最简写法

javascript 复制代码
function uniqueArray(arr){
    return [...new Set(arr)];
}
const arr = [1, 2, 3, 4, 4, 5, 5];
console.log(unique1(arr)); // [1,2,3,4,5]

优点:代码极简、性能优秀,时间复杂度 O (n)

缺点:无法处理对象、无法自定义去重规则

2. filter + indexOf 实现

javascript 复制代码
function uniqueArray(arr){ 
    return arr.filter((item,index) => arr.indexOf(item) === index); 
 }

原理: indexOf 永远返回元素第一次出现的下标,只有首次出现时下标匹配,保留元素。

缺点 :indexOf 底层遍历,时间复杂度 O (n²),大数据数组性能差。

3. reduce 累加去重

scss 复制代码
function uniqueArray(arr) {
    return arr.reduce((accumulator,current) => {
        if(!accumulator.includes(current)){
            accumulator.push(current);
        }
        return accumulator;
    },[]);
}

利用累加数组存储不重复值,includes 判断是否已存在,逻辑直观,但同样 O (n²),大数据不推荐。

4. 原生 for 循环两种写法

简易版(indexOf 判断)
scss 复制代码
function uniqueArray(arr) {
    const result = []
    for(let i = 0;i < arr.length;i++){
        if(result.indexOf(arr[i]) === -1) result.push(arr[i])
    }
    return result
}

优化版(对象哈希表,O (n) 性能)

arduino 复制代码
function uniqueArray(arr) {
    const result = []
    const map = {}
    for(const item of arr) {
        if(!map[item]) {
            map[item] = true
            result.push(item)
        }
    }
    return result
}

利用对象键快速标记已存在的值,查询速度远快于 indexOf,性能大幅提升;

缺陷: 数字、字符串会被统一转为对象字符串键,1 和 "1" 会判定为重复。

二、对象数组去重(业务最常用场景)

前端接口返回列表大多是对象数组,需要按指定字段去重,分三种场景:单字段、全对象、多字段联合去重。

1. 单字段去重(如 id 唯一)

arduino 复制代码
function uniqueByKey(arr,key) {
    const map = new Map()
    return arr.filter(item => {
        if(!map.has(item[key])){
            map.set(item[key],true)
            return true
        }
        return false
    })
}
const list = [{id:1,name:'A'},{id:1,name:'A'},{id:2,name:'B'}] 
console.log(uniqueByKey(list, 'id'))

2. 完整对象去重(全部属性一致才算重复)

将对象序列化为字符串作为唯一标识存入 Set:

vbnet 复制代码
function uniqueByObject(arr) {
    const set = new Set()
    return arr.filter(item => {
        const key = JSON.stringify(item)
        if(!set.has(key)) {
            set.add(key)
            return true
        }
        return false
    })
}

注意 :JSON.stringify 有局限:属性顺序不同、undefined / 函数会丢失,复杂对象慎用。

3. 进阶:单字段兼容基础数组 unique2

整合基础数组 + 单字段对象数组场景:

vbnet 复制代码
function unique2(arr,key) {
    if(!key) {
        return [...new Set(arr)]
    }
    const map = new Map()
    return arr.filter(item => {
        const value = item[key]
        if(!map.has(value)) {
            map.set(value,true)
            return true
        }
        return false
    })
}

三、终极通用去重函数 unique3(多字段 + 自定义比较)

前面所有方案都有局限:要么只能单字段、要么无法自定义重复规则。封装 unique3 支持三大模式:

  1. 不传配置:基础类型 Set 去重
  2. 传入 keys 数组:多字段联合去重
  3. 传入 compareFn:自定义函数判断重复

完整源码

javascript 复制代码
function unique3(arr,options = {}) {
    const { keys, compareFn } = options
    if(!keys && !compareFn) return [...new Set(arr)]
    if(keys) {
        const map = new Map()
        return arr.filter(item => {
            const key = keys.map(k => item[k]).join('|')
            if(!map.has(key)) {
                map.set(key,true)
                return true
            }
            return false
        })
    }
    if(compareFn) {
        return arr.filter((item,index,self) => {
            return index === self.findIndex(t => compareFn(t,item))
        })
    }
}

总结

  1. 简单基础数组:直接 [...new Set(arr)],最简高效;
  2. 对象单字段去重:uniqueByKey / unique2;
  3. 对象多字段联合去重:unique3({ keys: [...] });
  4. 复杂自定义重复规则:unique3({ compareFn });日常开发直接引入 unique3 万能函数,一套代码覆盖全部去重场景,不用重复写多种去重逻辑,大幅提升开发效率。
相关推荐
怕浪猫1 分钟前
分享一个做视频的skill,这条白板视频,每一笔都是代码画的
前端·javascript·面试
aa小小17 分钟前
大屏自适应缩放方案
前端·数据可视化
陪我去看海1 小时前
被要求猛出小程序,做了这个多平台多环境的部署替我承受压力
前端·微信小程序·抖音小程序
Frag0ut2 小时前
告别插件时代:HTML5 视频播放对比 Flash/Silverlight/ActiveX 的性能与技术优势解析
前端·html5·视频播放·flash·activex·流媒体技术·silverlight
用户818618028002 小时前
MongoDB文档模型设计——从建模到索引
前端
無名路人2 小时前
小程序点餐页吸顶滚动之分类按需加载,上划切换
前端·vue.js·微信小程序
竹林8183 小时前
把神经网络塞进一个浏览器标签页:端侧视觉 AI 的工程真相
前端·浏览器
乘风gg3 小时前
别跟风 AI 副业,工程师最该学的是 AI Coding
前端·ai编程·claude
Frag0ut3 小时前
深度解析:Chrome 自启动与后台常驻服务的作用、关闭方法及利弊权衡
前端·chrome
OpsEye3 小时前
当企业不再只关心能不能调用模型,而是关心值不值得调用
javascript·ai编程