js 数组去重(原始类型和引用类型)

原始类型去重

我们在接触编程的时候,或多或少都会接触过一些去除数组中重复项这种问题,每个语言有每个语言自己的去重方式,接下来我们来看看js中的数组去重可以用什么方式实现。

双层 for 循环

思路:

  1. 我们需要先创建一个数组 newArr 用来存放结果
  2. 对原数组进行遍历,将每一个元素与 newArr 中的每一项进行对比。
  3. 如果发现已经存在就直接 break 然后用 arr 的下一项与 newArr 中的每一项对比,如果遍历完之后没有相同的元素就将该项插入 newArr
js 复制代码
const arr = [1, 2, 3, 4, 2, 1]

function unique(arr) {
  let newArr = [] // 用来存放结果
  for (let i = 0; i < arr.length; i++) {
    for (var j = 0; j < newArr.length; j++) {
      // 判断新数组是否已经具有该数值
      if (arr[i] === newArr[j]) break
    }
    if (j === newArr.length) newArr.push(arr[i])
  }
  return newArr
}

console.log(unique(arr));

for + includes / indexOf

使用这种方法我们得先来了解一下数组中的 includes()indexxOf() 这两个方法。

  • includes() 方法用来判断一个数组是否包含一个指定的值,如果 则返回 true 则返回false

  • indexOf() 方法可返回数组中某个指定的元素位置。 该方法将从头到尾地检索数组,看它是否含有对应的元素。开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时)。如果找到一个 item,则返回 item 的第一次出现的位置。 开始位置的索引为 0。 如果在数组中没找到指定元素则返回 -1

思路:

  1. 先创建一个数组 newArr 用来存放结果
  2. 对原数组中的每一项进行遍历,将每一个元素使用includes / indexOf方法与newArr中的数据进行对比
  3. 如果存在就跳过该项让原数组下一项与 newArr 中的每一项进行对比,如果不存在则将该项放入 newArr 数组中
js 复制代码
const arr = [1, 2, 3, 4, 2, 1]

function unique(arr) {
  let newArr = []
  for (let i = 0; i < arr.length; i++) {
    // if (newArr.indexOf(arr[i]) === -1) newArr.push(arr[i])//时间复杂度O(n^2)因为indexOf会再循环一遍
    if (!newArr.includes(arr[i])) newArr.push(arr[i])//存在的话返回true,否则返回false
  }
  return newArr
}

sort + filter

看了上面的两种方法之后,大家可能会觉得上面这两种代码写的有点太长了,我们可不可以再精简一点,接下来我们直接用三行核心代码来解决这个问题。

思路:

  1. 先将数组进行排序
  2. 对排序后的数组进行遍历,如果前一项和后一项相同就过滤该项,否则就将该项放入新数组中
js 复制代码
const arr = [1, 2, 3, 4, 2, 1] 
function unique(arr) { 
  return arr.sort((a, b) => a - b).filter((item, index, array) => {
    return !index || item !== array[index - 1] 
  }) 
}
console.log(unique(arr));

tips:sort中一定要放回调函数指定排序顺序,否则可能会出错

filter + {}

哈希表相信大家都听过,简单来讲呢我们可以将它先看成是一个对象,而对象中的key是唯一的,我们现在的这个方法就是根据这个特性来实现的。

思路

  1. 先创建一个哈希表
  2. 使用filter()对数组中的元素进行过滤
  3. 如果存在于哈希表中就跳过,否则将其放入新数组中
js 复制代码
const arr = [1, 2, 3, 1, 2]

function unique(arr) {

  let obj = {}
  return arr.filter((item) => {
    return obj[item] ? false : obj[item] = true
  })// 时间复杂度O(n)
}
console.log(unique(arr));

Set

Set这种数据结构是ES6中新增的语法,这个数据结构有一个特点就是内部的元素是唯一的,根据这一特点我们只需要将数组放入Set中就会帮我们自动进行去重。

js 复制代码
const arr = [1, 2, 3, 1, 2]

function unique(arr) {
  return [...new Set(arr)]//时间复杂度O(2n)首先先遍历数组放入set,然后再遍历set放入[]
}
console.log(unique(arr));

引用类型去重

在js中的类型我们通常将其分为引用类型和原始类型,而他们之中最主要的区别就是引用类型是存放在堆中的,我们平常对引用类型的赋值通常都是直接给一个地址,取值的时候直接从对应的地址中取值即可。

当我们在数组中存放的都是引用类型的时候,就不能简单的根据数组的下标来进行内容的判断了,因为引用类型存放在数组中的话存放的是地址,但是我们要求的是将地址内部内容一样的给去除,接下来我们来实现一下。

思路:我们需要使用双层for循环的思路来对原数组进行去重,核心就是如何比较两个对象中内容是否相同。

js 复制代码
let arr = [
  { name: '张三', age: 18 },
  { name: '李四', age: 20 },
  { name: '王五', age: 22 },
  { name: '赵六', age: 24 },
  { name: '钱七', age: 26 },
  { name: '孙八', age: 28 },
  { name: '周九', age: 30 },
  { name: '张三', age: 18 }
]

function unique(arr) {
  let res = []
  for (let i = 0; i < arr.length; i++) {
    let flag = false
    for (let j = 0; j < res.length; j++) {
      if (equals(arr[i], res[j])) {
        flag = true
        break
      }
    }
    if (!flag) res.push(arr[i])
  }
  return res
}

上述代码是一个大致的流程,相信大家肯定都看得懂,就是将数组中的每一项遍历并且与res这个存放结果的数组中的元素进行比较,如果存在相同的则跳过,否则就插入。而代码的核心就是equals(arr[i], res[j]),接下来我们来为大家讲解一下这个函数中的逻辑。

思路:

  1. 首先我们的arr中存放的可能不止引用类型,还有原始类型,我们需要先对传入的两项进行判断,如果是原始类型则直接比较,否则就进行引用类型的比较
  2. 如果是引用类型,我们先判断两个引用类型中key的数量是不是一样,如果不是,那就一定不同返回false
  3. 如果传入的两个参数的key数量是一致的,那么我们就用 for in对两个引用类型进行遍历
  4. 在遍历两个引用类型的过程中,我们需要判断key是否在另一个对象中存在,如果不存在就返回false,否则,我们接着对该key对应的value进行比较
  5. 在对两个引用类型的value进行比较的过程中有可能里面还是引用类型,这时候我们就可以再次调用本身equals()来对value进行判断,这个过程就是我们所讲的递归

接下来给大家展现完整代码:

js 复制代码
let arr = [
  { name: '张三', age: 18 },
  { name: '李四', age: 20 },
  { name: '王五', age: 22 },
  { name: '赵六', age: 24 },
  { name: '钱七', age: 26 },
  { name: '孙八', age: 28 },
  { name: '周九', age: 30 },
  { name: '张三', age: 18 }
]

function unique(arr) {
  let res = []
  for (let i = 0; i < arr.length; i++) {
    let flag = false
    for (let j = 0; j < res.length; j++) {
      if (equals(arr[i], res[j])) {
        flag = true
        break
      }
    }
    if (!flag) res.push(arr[i])
  }
  return res
}

function equals(v1, v2) {
  // 判断是不是引用类型
  if ((typeof v1 === 'object' && v1 !== null) && (typeof v2 === 'object' && v2 !== null)) {
    // 判断key数量是否相等
    if (Object.keys(v1).length !== Object.keys(v2).length) return false
    for (let key in v1) {
      // 判断两者是否都有相同的key
      if (key in v2) {
        // 递归比较两者的value
        if (!equals(v1[key], v2[key])) return false
      } else {
        return false
      }
    }
    return true
  } else {
    return v1 === v2
  }
}

console.log(unique(arr))
相关推荐
五点六六六18 分钟前
Restful API 前端接口模型架构浅析
前端·javascript·设计模式
筱筱°21 分钟前
Vue 路由守卫
前端·javascript·vue.js
牛马baby21 分钟前
Java高频面试之集合-15
java·开发语言·面试
前端小张同学37 分钟前
前端Vue后端Nodejs 实现 pdf下载和预览,如何实现?
前端·javascript·node.js
VT.馒头1 小时前
【力扣】2666. 只允许一次函数调用——认识高阶函数
javascript·算法·leetcode·职场和发展
祈澈菇凉2 小时前
解释什么是受控组件和非受控组件
前端·javascript·react.js
徐小黑ACG2 小时前
使用vite新建vue3项目 以及elementui的使用 vite组件问题
前端·javascript·elementui
糕冷小美n2 小时前
Electron打包文件生成.exe文件打开即可使用
前端·javascript·electron
puppy0_02 小时前
【万字长文】前端如何处理计算密集型操作(数据量10w+)
前端·javascript
渗透测试老鸟-九青3 小时前
关于缓存欺骗的小总结
网络·经验分享·安全·web安全·缓存·面试