js 数组去重十几种解法,基础知识扎实吗?(附数组方法)

1,864 阅读6分钟

前言

看到有位掘友的座右铭,Talk is cheap, show me the code. 这句话很有意思。

一 、基本类型的数组去重

1、最直接高效的方法【扩展运算符 + Set()】

在实际工作中推荐直接使用这种方法去重,方便实惠。Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

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

上面其实相当于
let set = new Set(arr); return [... set];
这里set 是一个集合对象,通过...结构的方式,将每一项解构到数组中。

2、使用数组api reduce 方法【需要先排序】

下面的if条件可以这样写,if(prev.indexOf(cur) === -1) ,是indexOf()方法,prev里还没有,就push 近result.push, 有就不入result数组,

function removeDuplicate_2(arr) {
  arr.sort((a, b) => a - b) // 数组升序排序
  return  arr.reduce((prev, cur) => { // 这里return最后结果就是prev 的最后结果
  // 第一次数组为空时|| prev 数组的最后一项不等于arr 的下一项的话 ,把当前项cur push到prev数组中,
    if (prev.length === 0 || (prev[prev.length - 1] !== cur)) { 
      prev.push(cur)
    }
    return prev;  // 这里返回的prev 会成为 reduce(prev,cur)这里prev 的新值
  }, [])
}

reduce 方法介绍

一般写arr.reduce((p,c)=>{ fn content;return p },init),将下面的具体参数介绍对号入座。

概括:reduce参数包括一个函数和一个初始值,初始值可选,函数有四个参数可选,有初始值 时previousValue = 初始值(initialValue), currentValue = 数组第一项arr[0],无初始值时, p = arr [0], c = arr[1]。 return的结果作为下一次迭代的 p的初始值。

  • 一个 “reducer” 函数,包含四个参数:

    • previousValue:上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]
    • currentValue:数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]
    • currentIndex:数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。
    • array:用于遍历的数组。
  • initialValue 可选

  • 作为第一次调用 callback 函数时参数 previousValue 的值。若指定了初始值 initialValue,则 currentValue 则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue 将使用数组第二个元素。 用途

1、求数组所有值的和

let sum = [0, 1, 2, 3].reduce(function (previousValue, currentValue) {
  return previousValue + currentValue
}, 0)
// sum is 6

2 、累加对象数组里的值

let initialValue = 0
let sum = [{x: 1}, {x: 2}, {x: 3}].reduce(
    (previousValue, currentValue) => previousValue + currentValue.x
    , initialValue
)

console.log(sum) // logs 6

更多实例参考官方文档

看看有无初始值的情况
无初始值时 reduce() 如何运行

假如运行以下无初始值的 reduce() 代码:

const array = [15, 16, 17, 18, 19];

function reducer(previous, current, index, array) {
  const returns = previous + current;
  console.log(`previous: ${previous}, current: ${current}, index: ${index}, returns: ${returns}`);
  return returns;
}

array.reduce(reducer);

callback 被调用四次,每次调用的参数和返回值如下表:

callback iterationpreviousValuecurrentValuecurrentIndexarrayreturn value
first call15161[15, 16, 17, 18, 19]31
second call31172[15, 16, 17, 18, 19]48
third call48183[15, 16, 17, 18, 19]66
fourth call66194[15, 16, 17, 18, 19]85

由 reduce() 返回的值将是最后一次回调返回值(85)。

有初始值时 reduce() 如何运行

在这里,我们以相同的算法 reduce 同一个数组,但提供 10 作为初始值:

[15, 16, 17, 18, 19].reduce( (previousValue, currentValue, currentIndex, array) => previousValue + currentValue, 10 )

Copy to Clipboard

callback iterationpreviousValuecurrentValuecurrentIndexarrayreturn value
first call10150[15, 16, 17, 18, 19]25
second call25161[15, 16, 17, 18, 19]41
third call41172[15, 16, 17, 18, 19]58
fourth call58183[15, 16, 17, 18, 19]76
fifth call76194[15, 16, 17, 18, 19]95

这种情况下 reduce() 返回的值是 95

3、使用一个for循环解决【需要先排序】

1、声明一个新数组result来存放,去重后的数组 。

2、给数组排序。

3、循环排序后的数组, 若第i项不等于第i-1项,则把此项内容push到result数组, 注意这里和常规的,用当前项于下一项对比的方式不同, arr[i- 1] 这里是arr[-1],结果是undefined的,js 语言的特性,所以可以这样操作吧,思考一下(1),用if(arr[i] !== arr[i + 1])可以吗,评论区说出你的答案,为什么?

4、 返回结果result

function removeDuplicate_3(arr) {
  let result = []
  arr.sort((a, b) => a - b)
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] !== arr[i - 1]) {
      result.push(arr[i])
    }
  }
  return result
}

sort 方法介绍

 arr.sort((a, b) => a - b) // 升序
  arr.sort((a, b) => b - a) // 降序

4、使用数组api indexOf() 方法

function removeDuplicate_4(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (result.indexOf(arr[i]) == -1) {
      result.push(arr[i])
    }
  }
  return result;
}

indexOf() 方法介绍

indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
概括:接收两个参数,返回下标值。arr.indexOf(要查找的元素,从那个位置开始查找)

示例

var array = [2, 5, 9];
array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0

indexOf()方法详细介绍

5、使用数组api Array.from() 方法和 Set 对象

function removeDuplicate_5(arr) {
  return Array.from(new Set(arr))
}

Array.from()  方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

概括: 接收三个参数,返回数组。Array.from(要处理的类数组对象,fn【回调函数可选】, this【执行回调函数时的this指向,可选】)

注意: 使用的时Array.from ,而不是数组实例arr.from,类似于Java的静态方法用类名调用。

Array.From() 详细介绍

6、使用一个for循环 、利用对象的存储特性

function removeDuplicate_6(arr) {
  let result = [], obj = {};
  for (let item of arr) {
    if (!obj[item]) {
      result.push(item);
      obj[item] = 1
    } else {
      obj[item]++
    }
  }
  return result;
}

7、使用数组api filter 和indexOf 方法结合原型的this的call调用

function removeDuplicate_7(arr) {
  return Array.prototype.filter.call(arr, (item, index) => {
    return arr.indexOf(item) === index
  })
}

8 、利用数组快慢指针的方式

function removeDuplicate_8(arr) {
  let fast = 0, low = -1, result = [];
  arr.sort((a,b) => a - b)
  while (fast < arr.length) {
    if (arr[fast] !== arr[low]) {
      result.push(arr[fast])
    }
    fast++
    low++
  }
  return result;
}

二、数组对象类型的去重

9 、利用数组的filter对象和 Map 对象去重数组里的对象

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。 Map 对象

filter()  方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。


let arr_obj = [
  {
    id: 1,
    name: '张三',
    age: 18,
    sex: 1
  },
  {
    id: 2,
    name: '李四',
    age: 20,
    sex: 2
  },
  {
    id: 1,
    name: '张三',
    age: 18,
    sex: 1
  },
  {
    id: 3,
    name: '王老五',
    age: 18,
    sex: 2
  },
  {
    id: 4,
    name: '王老五',
    age: 18,
    sex: 2
  }
]
function removeDuplicate_10(arr, attr) {
  const result = new Map();
  return arr.filter((item) => !result.has(item[attr]) && result.set(item[attr], 1))
}

console.log(arr_obj);
console.log(removeDuplicate_10(arr_obj, 'name')); // 以对象姓名去重

上面filter()里面的内容会不好理解吗它相当于


function removeDuplicate_11(arr, attr) {
  const result = new Map();
  let fn = (item) => {
    if(!result.has(item[attr])) {
     return result.set(item[attr], 1)
    }
  }
  return arr.filter(fn)
}

!result.has(item[attr]) && result.set(item[attr], 1)) 相当于

 if(!result.has(item[attr])) {
     result.set(item[attr], 1)
 }
 //由于箭头函数没有带{}花括号,所以是有return的, 所以会相当于这样
  if(!result.has(item[attr])) {
     return result.set(item[attr], 1)
    }

去重前

[
  { id: 1, name: '张三', age: 18, sex: 1 },
  { id: 2, name: '李四', age: 20, sex: 2 },
  { id: 1, name: '张三', age: 18, sex: 1 },
  { id: 3, name: '王老五', age: 18, sex: 2 },
  { id: 4, name: '王老五', age: 18, sex: 2 }
]

去重后

[
  { id: 1, name: '张三', age: 18, sex: 1 },
  { id: 2, name: '李四', age: 20, sex: 2 },
  { id: 3, name: '王老五', age: 18, sex: 2 }
]

以id 去重

console.log(removeDuplicate_8(arr_obj, 'id'));
[
  { id: 1, name: '张三', age: 18, sex: 1 },
  { id: 2, name: '李四', age: 20, sex: 2 },
  { id: 3, name: '王老五', age: 18, sex: 2 },
  { id: 4, name: '王老五', age: 18, sex: 2 }
]

10 、使用数组方法reduce和对象特性

思路是创建一个空对象,判断对象里有没有该属性的值,没有就吧该项push 到数组存储

function removeDuplicate_12(arr, attr) {
  let obj = {}
  return arr.reduce((prev,curr) =>{
    // 这种三元运算符的方式是不是不好理解,看看下面10的做法,转化为if判断就好理解了。
    obj[curr[attr]] ? '' : obj[curr[attr]] = true && prev.push(curr) // ''为空的意思
     // 写法上也可以 为undefined 
    // obj[curr[attr]] ? undefined : obj[curr[attr]] = true && prev.push(curr)
    return prev
  },[])
}
console.log(removeDuplicate_12(arr_obj, 'name'));

11、使用for of 循环加对象特性。

思路和9的类似,知识迭代方式不同。

function removeDuplicate_13(arr, attr) {
  let obj = {}, result = [];
  for(let item of arr){
    if(!obj[item[attr]]) {
      result.push(item)
      obj[item[attr]] =  true
    }
  }
  return result;
}
console.log(removeDuplicate_13(arr_obj, 'id'));

相关文章参考

JS对象数组去重的三种方法

数组去重