数组去重的方式
现有如下数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let arr = [ {}, {}, '', '', 233, 233, '233', 'abc', undefined, undefined, null, null, NaN, NaN, [2], [2], [2, 3] ]
|
将使用以下几种方式分别实现数组去重
使用Set
数据结构
1 2 3 4 5 6
| Array.prototype.myUnique = function(){ return Array.from(new Set(this)); } console.log(arr.myUnique());
|
对于 {}、[] 这种引用数据类型并未实现去重。
使用for
循环以及include
方法
1 2 3 4 5 6 7 8 9 10 11 12
| Array.prototype.myUnique = function(){ let arr = []; for(let i = 0; i < this.length; i++){ if(!arr.includes(this[i])){ arr.push(this[i]); } } return arr; } console.log(arr.myUnique());
|
对于 {}、[] 这种引用数据类型并未实现去重。
使用for
循环以及indexOf
方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| Array.prototype.myUnique = function(){ let arr = []; for (let i = 0; i < this.length; i++) { if (arr.indexOf(this[i]) === -1) { arr.push(this[i]); } } return arr; }
console.log(arr.myUnique());
|
我们会发现indexOf方法并未识别到存在NaN,所以除了对于 {}、[] 这种引用数据类型,这种方式NaN也无法实现去重。
使用reduce
方法
1 2 3 4 5 6 7 8 9 10 11
| Array.prototype.myUnique = function(){ return this.reduce((accumulator, currentValue) => { if (!accumulator.includes(currentValue)) { accumulator.push(currentValue); } return accumulator; }, []) } console.log(arr.myUnique());
|
其实与使用for循环以及includes方法一样,只不过使用reduce会显得更加优雅。
在此简单回顾下reduce的使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 参数: 参数一: callback 函数(执行数组中每个值的函数,包含四个参数): prev 必需 (上一次调用回调返回的值,或者是提供的初始值(initialValue)) cur 必需(数组中当前被处理的元素) index 可选 (当前元素在数组中的索引) arr 可选 (调用 reduce 的数组) 参数二:initialValue 可选 (表示初始值,作为第一次调用 callback 的第一个参数。) 提供初始值,cur 从数组第一项开始,若不提供初始值,则 cur 从第二项开始执行,对应的第一次 prev 是数组第一项的值。 结论: 如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。 如果提供initialValue,从索引0开始。
注意: 1. reduce是一个对数组累积操作的方法,使用时要加上 return 返回累积操作的数据。这样 prev 才能获取上一次执行的结果,否则是 undefined;
2. 空数组执行 reduce 操作且不提供初始值时reduce会报错,错误信息如下: `Reduce of empty array with no initial value at Array.reduce`
|
使用filter
1 2 3 4 5 6 7 8
| Array.prototype.myUnique = function(){ return this.filter((v, i) => { return this.indexOf(v) === i; }) } console.log(arr.myUnique());
|
由于数组使用indexOf判断v所处的index位置都会返回-1,所以使用上述filter组合indexOf去重的方式会将NaN全部过滤掉了,切引用数据类型还是没有被去重。
使用JSON.stringify
1 2 3 4 5 6 7 8
| const array = [{id: 1}, {id: 2}, {id: 1}];
Array.prototype.myUnique = function(){ return Array.from(new Set(this.map(JSON.stringify)), JSON.parse); }
console.log(array.myUnique());
|
补充Array.from:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Array.from() 是 ECMAScript 6 中新增的一个方法,它可以从类数组对象或可迭代对象(如字符串、Set、Map、NodeList 等)创建一个新的数组实例。
基本语法:Array.from(arrayLike[, mapFn[, thisArg]]) arrayLike:必需,任何具有 length 属性且索引为整数的可迭代对象,或者是实现了 @@iterator 接口的对象。 mapFn:可选,一个映射函数,对每个元素调用该函数并将其结果收集到新数组中。 thisArg:可选,执行映射函数时的上下文对象。
示例:
let arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3}; let arr = Array.from(arrayLike); console.log(arr);
let divs = document.querySelectorAll('div'); let divArray = Array.from(divs); console.log(divArray.length);
let numbers = Array.from([1, 2, 3, 4], x => x * 2); console.log(numbers);
|
如果引用数据的顺序不一样,转的string就不等了。所以还是自己实现一个方法好一些。
使用自定义比较函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Array.prototype.myUnique = function () { const seen = new Map(); return this.filter(item => { if (typeof item === 'object' && item !== null) { const key = 'object' + Object.keys(item).sort().map(k => `${k}:${item[k]}`).join('|'); if (!seen.has(key)) { seen.set(key, true); return true; } } else { if (!seen.has(item)) { seen.set(item, true); return true; } } return false; }); } console.log(arr.myUnique());
|
这个方法利用了 Map 数据结构的特性,用键来存储数组中的元素,保证了元素的唯一性。对于对象,通过将属性名排序并拼接成字符串来判断是否重复。
这种方法适用于任何类型的数组,包括混合了基本数据类型和引用数据类型的数组。