从基础到进阶的数组去重方法详解

669 阅读4分钟

1. 引言

在实际开发过程中,我们经常需要处理包含重复元素的数组,除了简单的基础数组去重(只包含数字或字符串),还有对象数组等复杂数组,在面试中,对于数组去重,也是一个高频考点,因此掌握多种数组去重方法是非常必要的。

2. 基础去重方法

2.1:双重 for 循环

function removeDuplicates(arr) {
  for (let i = arr.length - 1; i > 0; i--) {
    for (let j = i - 1; j >= 0; j--) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
      }
    }
  }
  return arr;
}

const basicArray = [1, 2, 3, 4, 4, 5, 6, 6, 7];
console.log(removeDuplicates(basicArray)); // 输出: [1, 2, 3, 4, 5, 6, 7]

实现简单,易于理解,时间复杂度较高(O(n^2)),不适合大规模数据。

2.2:使用 Set 去重

function removeDuplicatesWithSet(arr) {
  return [...new Set(arr)];
}

const basicArray = [1, 2, 3, 4, 4, 5, 6, 6, 7];
console.log(removeDuplicatesWithSet(basicArray)); // 输出: [1, 2, 3, 4, 5, 6, 7]

简洁高效,时间复杂度较低(O(n)),仅适用于基本类型数组。

2.3 :使用 filter 去重

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

const basicArray = [1, 2, 3, 4, 4, 5, 6, 6, 7];
console.log(removeDuplicatesWithFilter(basicArray)); // 输出: [1, 2, 3, 4, 5, 6, 7]

简洁,易于理解,时间复杂度较高(O(n^2)),不适合大规模数据。

3. 面向对象数组的去重

在解决对象数组去重问题时,我们需要一个方法来判断对象之间的相等性。通常,JavaScript 的默认比较机制(如 ===)不能正确处理对象的比较,因为它们比较的是对象的引用而不是内容。因此,我们需要一个自定义的比较函数来递归地比较对象的所有属性。

  1. 遍历输入数组

    • 遍历输入的数组 arr 中的每一个元素。
    • 对于每一个元素,检查它是否已经在结果数组 res 中存在。
  2. 使用比较函数

    • 使用一个递归的比较函数 equals 来判断两个对象是否相等。
    • 如果两个对象都是基本类型,直接使用 === 比较。
    • 如果两个对象都是非空对象,则递归比较它们的所有属性。
  3. 添加唯一元素

    • 如果当前元素在结果数组中找不到重复项,则将其添加到结果数组中。
// 定义一个包含对象的数组
const arr = [
    {name: 'midsummer', age: '18', like: {n: 1}},
    {name: 'midsummer', age: '18', like: {n: 1}},
    {name: 'midsummer', age: '18'},
    {name: 'summer', age: '18'},
    {name: 'midsummer', age: '20'}
];

// 定义一个去重函数,用于处理对象数组
function uniqueArr(arr) {
    let res = [];
    
    for (let i = 0; i < arr.length; i++) {
        // 假设当前元素没有找到重复项
        let isFind = false;

        // 内层循环,遍历已去重的结果数组
        for (let j = 0; j < res.length; j++) {
            // 如果当前元素与结果数组中的某一项相等,则标记为已找到重复项
            if (equals(arr[i], res[j])) {
                isFind = true;
                break;
            }
        }
        // 如果当前元素没有找到重复项,则将其添加到结果数组中
        if (!isFind) {
            res.push(arr[i]);
        }
    }

    return res;
}

// 定义一个比较函数,用于判断两个对象是否相等
function equals(v1, v2) {
    // 如果两个对象都是非空对象,则递归比较它们的每个属性
    if ((typeof v1 === 'object' && v1 != null) && (typeof v2 === 'object' && v2 != null)) {
        // 遍历第一个对象的属性
        for (let key in v1) {
            // 检查第二个对象是否有相同的属性
            if (v2.hasOwnProperty(key)) {
                // 如果两个属性值不相等,则返回 false
                if (!equals(v1[key], v2[key])) {
                    return false;
                }
            } else {
                // 如果第二个对象没有相同的属性,则返回 false
                return false;
            }
        }
        // 如果所有属性都相等,则返回 true
        return true;
    } else {
        // 如果两个对象都是基本类型,则直接比较它们是否相等
        return v1 === v2;
    }
}

// 调用 uniqueArr 函数进行去重,并打印结果
console.log(uniqueArr(arr));
  1. uniqueArr 函数

    • 该函数接收一个对象数组 arr

    • 初始化一个空数组 res 用于存储去重后的结果。

    • 通过双重循环遍历数组中的每个元素:

      • 内层循环用于检查当前元素是否已经存在于 res 中。
      • 如果不存在,则将当前元素添加到 res 中。
  2. equals 函数

    • 该函数用于比较两个对象是否相等。

    • 如果两个对象都是非空对象,则递归比较它们的每个属性:

      • 如果所有属性都相等,则返回 true
      • 如果有任何属性不相等,则返回 false
    • 如果两个对象都是基本类型,则直接比较它们是否相等。

4. 总结

从基础的数字和字符串数组去重到复杂对象数组的去重,展示了多种方法和技术,包括双重 for 循环、Setfilter,以及通过递归比较函数 equals 来处理复杂对象数组去重的方法。通过理解不同去重方法背后的逻辑和适用场景,友友们可以灵活应对实际开发中的数据处理需求,并在面试中展示扎实的编程基础和算法思维。