js 数组去重十几种解法,基础知识扎实吗?(附数组方法)
前言
看到有位掘友的座右铭,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 iteration | previousValue | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 15 | 16 | 1 | [15, 16, 17, 18, 19] | 31 |
second call | 31 | 17 | 2 | [15, 16, 17, 18, 19] | 48 |
third call | 48 | 18 | 3 | [15, 16, 17, 18, 19] | 66 |
fourth call | 66 | 19 | 4 | [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 iteration | previousValue | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 10 | 15 | 0 | [15, 16, 17, 18, 19] | 25 |
second call | 25 | 16 | 1 | [15, 16, 17, 18, 19] | 41 |
third call | 41 | 17 | 2 | [15, 16, 17, 18, 19] | 58 |
fourth call | 58 | 18 | 3 | [15, 16, 17, 18, 19] | 76 |
fifth call | 76 | 19 | 4 | [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
复制代码
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的静态方法用类名调用。
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'));
复制代码
相关文章参考