js数组去重在前端面试中是经常被问道的问题,今天简单总结几种经典的数组去重方法以及一些大厂的面试题。

1、数组去重的常规方法

数组去重的常规思路总结如下:

  • Set + ...\Array.form => 无法去掉{}
  • 新数组 + indexOf方法 => 无法去掉NaN和{}
  • 新数组 + includes方法 => 无法去掉{}
  • filter + indexOf => 无法去掉{}, 两个NaN都会去掉, 不建议使用
  • 双重for循环 + splice方法 => 无法去掉NaN和{}
  • 新数组 + Map => 无法去掉{}
  • 新数组 + 对象 => 所有的都能去重
  • hasOwnProperty => 所有的都能去重

上述描述的测试数据:[1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]

数组去重的常规方法的代码如下:

// new Set + ...\Array.form
function arrFun(arr) {
  return [...new Set(arr)];
  // return Array.from(new Set(arr));
}

// indexOf or includes
function arrFun2(arr) {
  const array = [];
  // 或者下面if中的条件改为: !newArr.includes(item)
  if (array.indexOf(arr[i]) === -1) {
    array.push(arr[i]);
  }
  return arr;
}

// filter + indexOf
function arrFun3(arr) {
  return arr.filter((item, index, self) => {
    return self.indexOf(item) === index;
  });
}

// 两层循环 + splice
function arrFun5(arr) {
  let len = arr.length;

  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        len--; // 减少循环次数提高性能
        j--; // 保证j的值自加后不变
      }
    }
  }
  return arr;
}

// Map
function arrFun6(arr) {
  const map = new Map();
  const newArr = [];
  arr.forEach((item) => {
    if (!map.has(item)) {
      // has()用于判断map是否包为item的属性值
      map.set(item, true); // 使用set()将item设置到map中,并设置其属性值为true
      newArr.push(item);
    }
  });

  return newArr;
}

// 对象
function arrFun7(arr) {
  const newArr = [];
  const obj = {};
  arr.forEach((item) => {
    if (!obj[item]) {
      newArr.push(item);
      obj[item] = true;
    }
  });

  return newArr;
}

基于hasOwnProperty实现的数组去重如下:

function unique(arr) {
  var obj = {};
  return arr.filter(function(item, index, arr){
     return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
  })
}

2、数组去重进阶面试题

字节曾经有过这样一道面试题:{id: 1, name: 'admin'} 和 {id: 1, name: 'admin'} 应该是相同的(id、name都相同表明是同一个用户)请问这样的数据如何去重? 具体思路如下:

/**
 * 数组去重
 * 原始值使用严格相等比较, 对象值递归比较其所有属性, 属性数量和属性名称必须一致
 * 数组中的对象均为plain object
 * @param {Array} arr
 * @return {Array}
 */
// 不使用任何标准库, 完全手写去重过程
function uniqueArray(arr){
  let res = [];

  for (let i = 0; i < arr.length; i++) {
    let isFind = false;
    for (let j = 0; j < res.length; j++){
      // 下面的判断是自定义的判断, 通过该判断实现了题目的要求(原始值严格相等比较, 对象值递归比较)
      if(equals(res[j], arr[i])){
        isFind = true;
        break;
      }
    }
    if (!isFind) {
      res.push(arr[i]);
    }
  }

  return res;
}

function equals(v1, v2) {
  return JSON.stringify(v1) === JSON.stringify(v2);
}

console.log(uniqueArray([1, 2, 2, 2, 3]));  // [ 1, 2, 3 ]
console.log(uniqueArray([{id: 1, name: 'admin'}, {id: 1, name: 'admin'}]))  // [ { id: 1, name: 'admin' } ]