Skip to content

去重的核心:要根据实际业务怎么定义'重'

基本数据类型

js
const arr = [1, 2, 1, 3];
//用Set去重
const uniqueArr = [...new Set(arr)]; // [1, 2, 3]
//用filter去重
const uniqueArr = arr => arr.filter((item, index) => arr.indexOf(item) === index); // 相同的元素只保留一个

//indexOf
const uniqueArr = (arr)  =>{
   let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    if (newArr.indexOf(arr[i]) === -1) {//或 !newArr.includes(arr[i])
      newArr.push(arr[i]);
    }
  }
  return newArr;
}

//==> reduce与 includes
const uniqueArr = (arr)  => arr.reduce((newArr,item) => newArr.includes(item) ? newArr : [...newArr,item],[])

//双重循环遍历数组去重
const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [];
for (let i = 0; i < arr.length; i++) {
  let isDuplicate = false;
  for (let j = 0; j < uniqueArr.length; j++) {
    if (arr[i] === uniqueArr[j]) {
      isDuplicate = true;
      break;
    }
  }
  if (!isDuplicate) {
    uniqueArr.push(arr[i]);
  }
}
console.log(uniqueArr); // [1, 2, 3]

如果要去重一个包含不同数据类型的数组,需要添加一些额外的检查。

Date类型转换成Unix时间戳,RegExp类型转换成字符串。

js
const arr = ['a', 'b', 1, 1, 'a', /abc/, /abc/, new Date(), new Date()]

const isObject = val => Object(val) === val// 判断是否为对象
function uniqueArr(arr) {
  return [...new Set(arr.map((item) => {
    if (!isObject(item))
      return item // 不是
    if (item instanceof Date)
      return item.getTime()
    else if (item instanceof RegExp)
      return item.toString()
  }))]
}
uniqueArr(arr) // ['a', 'b', 1, '/abc/', 1678519417983]

对象数组去重

对象相同比较

基本思路

  1. 首先判断两个值的类型是否相同,如果不同,则返回 false
  2. 处理特殊情况 null、undefined、Date、function
  3. 处理数组类型,对比项的数量,再递归对比每个项是否相等;
  4. 处理朴素对象,对比键的数量,相等则再递归对比值是否相等。
js
function isEqual(obj1, obj2) {
  const equalType = (val1, val2) => Object.prototype.toString.call(val1) !== Object.prototype.toString.call(val2)
  // 判断类型是否一致
  if (equalType(obj1, obj2)) {
    return false
  }

  // 判断 null 和 undefined:只要有一个为 null 或 undefined,则直接比较值
  if (obj1 === null || obj2 === null || obj1 === undefined || obj2 === undefined) {
    return obj1 === obj2
  }

  // 判断基本类型是否相等
  if (typeof obj1 === 'number' || typeof obj1 === 'string' || typeof obj1 === 'boolean') {
    return obj1 === obj2
  }
  if (typeof obj1 === 'function' && typeof obj2 === 'function')
    return obj1.toString() === obj2.toString()

  if (obj1 instanceof Date && obj2 instanceof Date)
    return obj1.getTime() === obj2.getTime()

  // 判断数组是否相等
  if (Array.isArray(obj1)) {
    if (!Array.isArray(obj2) || obj1.length !== obj2.length) {
      return false
    }
    return obj1.every((item, index) => isEqual(item, obj2[index]))
    // for (let i = 0; i < obj1.length; i++) {
    //   if (!isEqual(obj1[i], obj2[i])) {
    //     return false;
    //   }
    // }
    // return true;
  }
  // 比较对象是否相等
  // Object.keys: 返回对象自身的所有可枚举的属性的键名,不包含 Symbol 类型键名
  // Reflect.ownKeys(): 返回所有类型的键名,包括常规键名和 Symbol 键名。
  const keys1 = Object.keys(obj1)
  const keys2 = Object.keys(obj2)
  if (keys1.length !== keys2.length) {
    return false
  }
  for (const key of keys1) {
    if (!isEqual(obj1[key], obj2[key])) {
      return false
    }
  }
  return true
}
测试示例
js
// 示例
const obj1 = {
  name: 'Tom',
  age: 18,
  hobby: ['reading', 'music'],
  address: {
    province: 'Guangdong',
    city: 'Shenzhen',
    detail: {
      street: 'Xinxi Road',
      number: 888
    },
    date: new Date('2022-02-13'),
    func() {
      console.log(1)
    }
  }
}
const obj2 = {
  name: 'Tom',
  age: 18,
  hobby: ['reading', 'music'],
  address: {
    province: 'Guangdong',
    city: 'Shenzhen',
    detail: {
      street: 'Xinxi Road',
      number: 888
    },
    date: new Date('2022-02-13'),
    func() {
      console.log(1)
    }
  }
}
const obj3 = {
  name: 'Tom',
  age: 18,
  hobby: ['reading', 'music', 'dance'],
  address: {
    province: 'Guangdong',
    city: 'Shenzhen',
    detail: {
      street: 'Xinxi Road',
      number: 888
    },
    // date:new Date('2022-02-13'),
    // func:function(){
    //  console.log(1)
    // }
  }
}
isEqual(obj1, obj2) // true
isEqual(obj1, obj3) // false
isEqual(undefined, null)// false
isEqual(null, null)

对象数组去重

js

const arr = [1,2,3,1]

//深拷贝
function deepClone(obj){
  if(obj === null || typeof obj !== 'object') return obj
  if(obj.constructor === Date) return new Date(obj);
  if(obj.constructor === RegExp) return new RegExp(obj);
  let newObj = Array.isArray(obj) ? [] : {}
  for(let key in obj){
    if(obj.hasOwnProperty(key)) newObj[key] =  deepClone(obj[key])
  }
  return newObj
}

const uniqueArr(arr){
  const newArr = deepClone(arr)
  let len = newArr.length
  for(let i = 0; i < len; i++){
    for(let j = i + 1; j < len; j++){
      if(isEqual(newArr[i], newArr[j])){
        newArr.splice(j,1)
      }
    }
  }
  return newArr
}
测试数据
js
const objArr1 = [
  { a: 1, b: 2 },
  { a: 2, b: 3 },
  { b: 2, a: 1 },
  { a: 3, b: 2 },
  [1, 2, 3],
  [4, 5, 6],
  [2, 1, 3],
  {
    name: 'Tom',
    age: 18,
    hobby: ['reading', 'music'],
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Xinxi Road',
        number: 888
      },
      date: new Date('2022-02-13'),
      func() {
        console.log(1)
      }
    }
  },
  {
    name: 'Tom',
    age: 18,
    hobby: ['reading', 'music'],
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Xinxi Road',
        number: 888
      },
      date: new Date('2022-02-13'),
      func() {
        console.log(1)
      }
    }
  },
  {
    name: 'Tom',
    age: 18,
    hobby: ['reading', 'music', 'dance'],
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Xinxi Road',
        number: 888
      },
    // date:new Date('2022-02-13'),
    // func:function(){
    //  console.log(1)
    // }
    }
  },
  {
    name: 'Tom',
    age: 18,
    hobby: ['reading', 'music', 'dance'],
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Xinxi Road',
        number: 888
      },
      date: new Date('2022-02-13'),
      func() {
        console.log(2)
      }
    }
  },
  {
    name: 'Tom',
    age: 18,
    hobby: ['reading', 'music', 'dance'],
    address: {
      province: 'Guangdong',
      city: 'Shenzhen',
      detail: {
        street: 'Xinxi Road',
        number: 888
      },
      date: new Date('2022-02-11'),
      func() {
        console.log(2)
      }
    }
  }
]
// uniqueArr(objArr1)