JS数组去重

数组去重

数组去重是非常经典的问题了,不同的实现方法,性能要求差别非常大。

假设有这样的数组
去掉重复的项,只保留一个

let arr = [0, 1, 2, 2, 3, 3, 3, 4];
let filterArr = unique(arr)
filterArr => [1,1,1,2,2,2,3,3];

实现方案一:

使用双for循环遍历,大概思路。外层循环从数组第0项开始遍历,内层循环从第i+1项开始遍历到数组最后的位置,如果有重复的项就删除。

const unique = (arr) => {
	for (var i = 0; i < arr.length - 1; i++) {
	    for (var j = i + 1; j < arr.length; j++) {
	        if (arr[i] === arr[j]) {
	            arr.splice(j, 1); 
	            j--; // 改变了数组长度以后让循环留在原位
	        }
	}
	return arr;
}

这里要特别注意的是j–这个操作,因为splice删除数组某一项会导致数组挪位,如果出现连续重复的项,就会出现问题。
该方法非常消耗性能,时间复杂度为O(n^2)



实现方案二:

2.通过对象属性名唯一去重
思路是对象的属性名唯一,重复的属性名会被覆盖,遍历数组的每一项,以对象属性名的方式保存,如果已经存在就删去。

const unique = (arr) => {
	let obj = {};
	for (let i = 0; i < arr.length; i++){
		if (typeof obj[arr[i] !== 'undefined') { // 数组中已经出现过
			arr.splice(i, 1);
			i--;
			continue;
		}
		// 第一次出现就保存到对象中
		obj[arr[i]] = arr[i];
	}
	return arr;
}

这种方式相对比较节省性能,但是有缺陷,想象一下如果属性名为’1’和1,不论哪一种都会覆盖彼此。

意思是:
如果数组长这样

let arr  = [0, 1, '1', '1', 2];
console.log(unique(arr)); //=> [0, 1, 2]

因为不论是数字类型还是字符串类型,最终都以字符串形式保存入对象。


实现方案三:

 const unique = (arr) => {
        // 考虑空数组,若非空则放入第一项到新数组
        var item = typeof arr[0] === 'undefined' ? [] : [arr[0]];
        for (var i = 1; i < arr.length; i++) {
            // 如果不存在就将该项推入数组,存在就不继续执行
            item.indexOf(arr[i]) === -1 && item.push(arr[i])
        }

        return item;
    }

核心思想:利用indexOf()在新数组中查找,如果不存在返回-1的特性。



利用indexOf另外一个特性,返回数组中该值第一次出现的索引值,利用forEach每次循环的index匹配,如果是第一次出现的就压入新数组。

const unique = (arr) => {
  let item = [];

  arr.forEach((e, index) => {
    if(index === arr.indexOf(e)) {
      item.push(e);
    }
  })
  return item;
}

但是indexOf的内部实现原理也是for循环,所以跟双for循环类似,都非常消耗性能。

实现方案四:

es6也提供了一些方法更加简洁。

对于Set这个类,摘自MDN
JS数组去重_第1张图片
连NaN都能做到不重复!!

以上我们知道Set中的元素是唯一的,也就是其包含的每个value是唯一的

const unique = (arr) => [...new Set(arr)];
// or
const unique = (arr) => Array.from(new Set(arr));

Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组,个人更喜欢展开运算符。

如果有更好的方法再来补充~

你可能感兴趣的:(JS)