前端实战:数组去重七种武器大揭秘!

43 阅读5分钟

❤ 写在前面
如果觉得对你有帮助的话,点个小❤❤ 吧,你的支持是对我最大的鼓励~
个人独立开发wx小程序,感谢支持! small.png


前言:为什么需要数组去重?

想象一下,你正在整理一个装满各种颜色袜子的抽屉。你会发现有很多双相同颜色的袜子,为了节省空间,你会把重复的袜子拿出来。在前端开发中,处理数据时也会遇到类似情况——数组中的重复元素会浪费资源、影响性能,甚至导致数据错误。

今天,我们就来探索前端开发中数组去重的七种方法,每种方法都有其独特的“武器特性”!

方法一:Set大法(ES6推荐)

这是目前最简洁、最高效的方法,就像一把锋利的瑞士军刀!

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]

原理图:

原始数组 → Set容器(自动去重) → 展开为数组 → 去重后的数组

优点: 代码简洁,性能优秀 缺点: 无法处理特殊对象去重(如:对象、NaN等特殊情况需注意)

方法二:Filter + IndexOf(经典组合)

这种方法像是侦探办案,检查每个元素是否是第一次出现:

function unique(arr) {
    return arr.filter((item, index) => {
        return arr.indexOf(item) === index;
    });
}

const arr = ['苹果', '香蕉', '苹果', '橙子', '香蕉'];
console.log(unique(arr)); // ['苹果', '香蕉', '橙子']

流程图:

开始
  ↓
遍历数组每个元素
  ↓
检查当前元素首次出现位置是否等于当前位置
  ↓
是 → 保留元素
  ↓
否 → 过滤掉
  ↓
返回新数组

方法三:Reduce累积器

使用reduce像是用漏斗过滤,只保留第一次遇到的元素:

const arr = [1, 2, 2, 3, 3, 3, 4];
const uniqueArr = arr.reduce((acc, current) => {
    if (!acc.includes(current)) {
        acc.push(current);
    }
    return acc;
}, []);

console.log(uniqueArr); // [1, 2, 3, 4]

方法四:双层循环(最原始的方法)

这是最基础的实现方式,就像手动检查每双袜子:

function unique(arr) {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        let isDuplicate = false;
        for (let j = 0; j < result.length; j++) {
            if (arr[i] === result[j]) {
                isDuplicate = true;
                break;
            }
        }
        if (!isDuplicate) {
            result.push(arr[i]);
        }
    }
    return result;
}

方法五:Object键值法

利用对象的键名不可重复的特性:

function unique(arr) {
    const obj = {};
    const result = [];
    
    arr.forEach(item => {
        if (!obj[item]) {
            obj[item] = true;
            result.push(item);
        }
    });
    
    return result;
}

⚠️ 注意: 这种方法会将数字和字符串视为相同键,如 1'1' 会被认为是重复的。

方法六:Map数据结构

Map比Object更适合处理复杂类型的去重:

function unique(arr) {
    const map = new Map();
    const result = [];
    
    arr.forEach(item => {
        if (!map.has(item)) {
            map.set(item, true);
            result.push(item);
        }
    });
    
    return result;
}

// 可以处理对象引用去重
const obj1 = {name: '张三'};
const obj2 = {name: '张三'};
const arr = [obj1, obj2, obj1];
console.log(unique(arr).length); // 2(obj1只出现一次,obj2是不同引用)

方法七:排序相邻去重法

先排序,然后比较相邻元素:

function unique(arr) {
    const sortedArr = [...arr].sort();
    const result = [sortedArr[0]];
    
    for (let i = 1; i < sortedArr.length; i++) {
        if (sortedArr[i] !== sortedArr[i - 1]) {
            result.push(sortedArr[i]);
        }
    }
    
    return result;
}

特殊场景处理

1. 对象数组去重

// 根据对象的某个属性去重
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 users = [
    {id: 1, name: 'Alice'},
    {id: 2, name: 'Bob'},
    {id: 1, name: 'Alice'}, // 重复ID
];
console.log(uniqueByKey(users, 'id')); // 前两个对象

2. 处理NaN的重复

// Set可以正确处理NaN去重
const arr = [NaN, NaN, 1, 2, 1];
console.log([...new Set(arr)]); // [NaN, 1, 2]

性能对比实验

让我们通过一个简单的性能测试来看看各种方法的效率差异:

// 生成测试数据
const testArray = [];
for (let i = 0; i < 10000; i++) {
    testArray.push(Math.floor(Math.random() * 1000));
}

// 测试函数执行时间
function testPerformance(fn, arr) {
    const start = performance.now();
    fn(arr);
    const end = performance.now();
    return end - start;
}

// 测试结果通常如下(时间从短到长):
// 1. Set方法 √
// 2. Object键值法
// 3. Map方法
// 4. Filter + IndexOf
// 5. Reduce方法
// 6. 排序相邻法
// 7. 双层循环

选择合适的方法:决策流程图

开始选择去重方法
        ↓
需要考虑性能吗? → 是 → 使用Set方法(最快)
        ↓否
数组中有对象吗? → 是 → 使用Map或根据属性去重
        ↓否
需要兼容老浏览器吗? → 是 → 使用Filter+indexOf或Object键值法
        ↓否
代码简洁更重要吗? → 是 → 使用SetReduce
        ↓否
使用Filter+indexOf(平衡选择)

总结与最佳实践

  1. 现代项目首选[...new Set(arr)] - 简洁高效
  2. 对象数组去重:使用Map或根据特定属性去重
  3. 兼容性要求:Filter + IndexOf或Object键值法
  4. 性能敏感场景:Set方法最快,其次是Object键值法
  5. 代码可读性:Reduce方法语义清晰,适合函数式编程

实战小挑战

试试这个综合题目:

// 有一个混合类型的数组,如何去除所有类型的重复?
const mixedArray = [
    1, '1', 1, 
    true, 'true', true,
    null, undefined, null,
    {a: 1}, {a: 1}, // 注意:这两个对象看起来一样,但引用不同
    [1, 2], [1, 2]
];

// 你的去重策略是什么?

提示: 可能需要结合多种方法,或者自定义比较函数!

结语

数组去重是前端开发中的基础但重要技能。不同的场景需要不同的方法,就像工具箱里的不同工具,各有各的用途。掌握这些方法,不仅能提高代码效率,还能让你的解决方案更加优雅。

希望这篇博客能帮助你在下次遇到数组去重问题时,能够自信地选择最合适的方法!