携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
数组去重
数组去重是一个很常见的需求,而实现的方式也多种多样,接下来我们来看看有哪些实现方式。
一、普通方法
创建一个空数组 arr,遍历原始数组(this)的每一项,如果当前项不存在 arr 中,则 push 进 arr
缺点:这里其实是两层遍历。第一层是外层的 for 循环,第二层是内部的 indexOf 判断
Array.prototype.unique = function() {
var arr = [];
var len = this.length;
for(var i=0; i<len; i++) {
if(arr.indexOf(this[i]) === -1) arr.push(this[i]);
}
return arr;
}
二、较快的方式
使用对象(hash)的方式来记录是否已经存在
Array.prototype.unique = function() {
var json = {};
var arr = [];
var len = this.length;
for(var i=0; i<len; i++) {
if(typeof json[this[i]] === "undefined") {
json[this[i]] = true;
arr.push(this[i]);
}
}
return arr;
}
但是有时我们会发现上述方法失灵了,例如数组:[1, "1", 2, 2]
我们希望去重之后得到的结果是:[1, "1", 2],但是会发现结果变成了 [1, 2]
这其实是因为我们在使用 hash 的时候,就是把数组元素作为 hash 的 key 值了,那么在使用过程中就会把数组元素变成字符串。
- 解决方案:使用
Object.prototype.toString.call
可以具体判断出 key 的数据类型
json[this[i]] = {}; // json[1] = {}
// json[1]["[object Number]"] = 1
// json[1]["[object String]"] = 1
json[this[i]][Object.prototype.toString.call(this[i])] = 1;
json 中的每一项,也都是一个对象。内部对象的 key 值为当前循环项的类型,外部对象(json)的 key 值为当前循环项的字符串内容
例如:[1, "1"],在 json 的存储中就会变为
json = {
1: {
"[object Number]": 1, // value赋值为1,就可以统计重复的数量了,具有更好的扩展性
"[object String]": 1
}
}
Array.prototype.unique = function() {
var json = {}, arr = [], len = this.length;
for(let i=0; i<len; i++) {
var type = Object.prototype.toString.call(this[i]); // 类型
if(typeof json[this[i]] === "undefined") { // 如果json[1]为undefined,则对其初始化
json[this[i]] = {}; // 初始化为空对象
json[this[i]][type] = 1; // json[1]["[object Number]"] = 1,记录为1
arr.push(this[i]); // 在新数组中添加当前项
} else if(json[this[i]][type] === "undefined") { // json层的对象已经初始化过了,但内部type为key的内容还没记录过
// 类似于:json[1]["[object Number]"]有值,但json[1]["[object String]"]未定义
json[this[i]][type] = 1;
arr.push(this[i]);
} else { // 剩下的就是定义过的,直接+1就行了
json[this[i]][type]++;
}
}
}
缺点:不能为对象去重。如果传入的数组为 [{ a: 1 }, {}],则去重的结果就是 [{ a: 1 }],因为对象转为字符串后都是 [object Object]
三、排序后对比
- 先对数组进行排序:可以让相同的数据挨在一起
- 定义一个新的空数组
- 遍历排序后的数组
- 如果当前项等于新数组的最后一项,则跳过
- 如果不等于,则将当前项插入新数组
Array.prototype.unique = function() {
this.sort(); // 先排序
var arr = [], len = this.length;
for(var i=0; i<len; i++) {
if(this[i] !== arr[arr.length - 1]) { // 如果当前项不等于arr的最后一项
arr.push(this[i])
}
}
return arr
}
四、利用ES6的Set
function unique(arr) {
return [...new Set(arr)];
}
不考虑兼容性的话,这种去重的方法代码量最少。
const arr = [1, "1", {}, {}];
console.log(unique(arr)); // [ 1, '1', {}, {} ]
通过上述代码的打印结果,可以看出 new Set 的方法可以区分出数据类型,但是无法去掉相同对象。(本质上是因为对象都是引用)
五、利用Map去重
function unique(arr) {
const map = new Map();
const array = [];
for (let i = 0; i < arr.length; i++) {
if (map.has(arr[i])) { // 如果已经存在了,则将其值设为 true(说明是重复元素)
map.set(arr[i], true);
} else { // 否则将其值设为false(目前没重复)
map.set(arr[i], false);
array.push(arr[i]);
}
}
return array;
}