js 数组去重

前段时间面试被问到数组有哪些去重的方式, 除了双重for循环我竟然想不出第二个, 惭愧惭愧. 前两年的工作都是html+css, css3, h5, 响应式页面, 外加一些cms, jQuery, bootstrap等. 感觉一直在前端领域的边缘徘徊, js基础薄弱, 更别说es6了,  现在换的这份工作才感觉刚刚步入正轨, 等待我学习的领域无限宽广 

1. 双重for循环 (不能过滤掉 NaN、Object)

双重for循环 + splice的方式在人群中应该是最高龄的方法了(年代久远).  

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
for (let i = 0; i < arr.length; i++) {
	for (let j = i + 1; j < arr.length; j++) {
		if (arr[i] === arr[j]) {
			arr.splice(j, 1);
			j--;	//splice会改变原始数组, 删除重复元素后仍需从上次位置与前一个比较, 所以j--
		}
	}
}
console.log(arr)	// [1, 2, 3, 4, 5, 6, 7, 8, 9]

上面有可以优化的地方, 如果数据量很大, 每次循环都会计算arr.length的值, 非常影响性能

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arrLength = arr.length;		//循环之前创建一个变量存储数组长度

for (let i = 0; i < arrLength; i++) {
	for (let j = i + 1; j < arrLength; j++) {
		if (arr[i] === arr[j]) {
			arr.splice(j, 1);
			j--;
			arrLength = arr.length;		//删除元素后需要重新获取数组长度, 否则很容易出现死循环(undefined == fundefined)
		}
	}
}

console.log(arr)		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

2. indexOf去重 (不能过滤掉 NaN、Object)

indexOf()去重思路是: 创建一个空数组, 原数组的元素不在新建的空数组中, 就push()进去

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arr2 = []

for (let i = 0; i < arr.length; i++) {
	if (arr2.indexOf(arr[i]) === -1) {		//indexOf()在数组中没找到指定元素则返回 -1
		arr2.push(arr[i]);
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

for in循环一样的道理, 不过for in循环的效率很低, 推荐使用for循环

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arr2 = []

for (i in arr) {
	if (arr2.indexOf(arr[i]) === -1) {
		arr2.push(arr[i])
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

3. includes去重 (不能过滤掉 Object)

includes()去重思路和indexOf()类似, 只是api不同而已

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arr2 = []

for (let i = 0; i < arr.length; i++) {
	if (!arr2.includes(arr[i])) {        //includes()数组中查不到元素返回false
		arr2.push(arr[i])
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

4. 利用对象key值唯一的特性去重 (可以过滤掉 Object, 但是不能区分NaN和'NaN')

这种方法本质原理和indexOf(), includes() 相同, indexOf(), includes()是判断某个元素是否在数组中, 此种方式以数组元素作为对象的key来判断.

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arr2 = [];
let obj = {};

for (let i = 0; i < arr.length; i++) {
	if (!obj[arr[i]]) {			//js对象获取属性有两种方式:   .  和  []
		obj[arr[i]] = 'a';		//随便赋一个值, 这样对象中就存在arr[i]这个key了
		arr2.push(arr[i])
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

5. sort() 去重 (不能过滤NaN、Object)

sort去重的思路就是, 先排序再比较, 这样就不用像双重for循环那样每两个都要比较一次了

let arr = [9, 9, 9, 1, 6, 1, 6, 6, 1, 7, 1, 2, 3, 4, 5, 8, 5, 5];
let arr2 = [];

arr.sort();
if (arr.length > 0) {		//以下相邻元素比较会少push第一个, 所以比较前先把第一个push进arr2
	arr2.push(arr[0])		
}
for(let i = 1; i < arr.length; i++) {
	if(arr[i] != arr[i - 1]) {
		arr2.push(arr[i])
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

6. ES6的Set (不能过滤掉 Object)

Set类似于数组,但是成员的值是唯一的, Set本身是一个构造函数,用来生成 Set 数据结构。

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];

arr = Array.from(new Set(arr))	  //Array.from()将拥有 length 属性的对象或可迭代的对象转换为数组

console.log(arr);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

也可以这样:

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];

arr = [...new Set(arr)]   	//思路: 数组转Set, Set去重后再转成数组

console.log(arr);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

7. ES6的Map (不能过滤掉 Object)

Map它类似于对象, 但是key不限于字符串, 可以理解是对象的扩展. 同样, map中的key不会重复, map去重数组和第4个原理完全相同.

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];
let arr2 = [];
let map = new Map();

for (let i = 0; i < arr.length; i++) {
	if (!map.has(arr[i])) {
		map.set(arr[i]);
		arr2.push(arr[i])
	}
}

console.log(arr2);		//[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

8. 利用递归去重

递归去重本质和双重for循环相同, 和第五个也类似, 只是这里是递归.

let arr = [1, 1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 9];

function test(num) {
	if (num >= 1) {
		if (arr[num] == arr[num - 1]) {
			arr.splice(num, 1)
		}
		test(num - 1)
	}
}
test(arr.length - 1)

console.log(arr)

 

总结:

总体看来, 数组去重的思路无外乎两种:

1. 两两比较然后删除其一或者赋值到新数组, 区别就在于两两比较的方法不同

2. 特定的api, 如Set.

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值