js之数组去重

面试常考的点之一吧(我也只被考过set的用法而已)

先上最简单版~①利用set

[... new Set(arr)]

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。new Set(array)得到的是一个Set数组(没有重复值),三个点为扩展运算符,得到用逗号分隔的参数序列,最外的中括号使得我们最终得到的还是一个数组。这句话也可以写成Array.from(new Set(arr))。不能写成Array(new Set(arr)),否则会转化成[Set(size)],因为new Set(arr)得到的是一个Set,如果你还想得到数组必须Array(... new Set(arr)),但这其实已经和[... new Set(arr)]没有区别了。在此我们复习下Array.of(3)和Array(3)的区别。

Array.of(3); // [3]
Array(3); // [ , , ]

如果参数是两个及两个以上,他们表示的东西没有区别。

我们再来复习下扩展运算符,除了运用于数组去重,扩展运算符还可以展开数组,所以不再需要apply方法将数组转为函数的参数。我们来看两个例子

// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
//  等同于
Math.max(14, 3, 77);
// ES5 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);
注意push()函数后面可以跟多个参数!

提到Set不由得让人想起他的好兄弟Map,在有Map之前,对象Object的键(属性)只能是字符串,如果不是字符串就会调用toString()方法将其转换成字符串。为了解决这个问题,Map应运而生,甚至连数组、函数、对象都可以当Map的键。

var map = new Map();

var k1 = ['a'];
var k2 = ['a'];

map.set(k1, 111);
map.set(k2, 222);

map.get( k1 );  //111
map.get( k2 );  //222
以上这个例子也说明了尽管k1、k2值相等,但他们指向的位置不同,所以在Map中也是两个不同的键。


提到Map不由得让人想到Object,所以接下来引出我们的第二种方法:采用对象keys方法,对象中的 key 永远不会重复

        Array.prototype.unique = function(){
            var obj = {};
            //这个地方不能用for in因为for/in循环会遍历所有可枚举的属性
            //包括自有和继承两种
            //而且for/in中i代表的是index
            //可以用for/of
            //此时的i代表数组第i个值
            for(var i of this){
                obj[i] = i;
            }
            // this.forEach(function(element) {
            //     obj[element] = element;
            // }, this);
            return Object.keys(obj);
        }

Object.keys(obj)返回对象的所有属性组成的数组,不包括原型方法和属性,也不能遍历到不可枚举的属性。此处插入一句:for in用来遍历对象,可以遍历到原型上的方法和属性,但是不能遍历到不可枚举的属性,当然如果不想要可以用hasOwnProperty判断一下是不是自有属性。

for (var key in object) {
  if(object.hasOwnProperty(key)){
    console.log(key);
  }
}

for of更适合用于数组,因为他不会遍历到原型上的属性和自己本身的索引,只会遍历数组内的元素。


这个地方用到了forEach(),在这里我又踩到了一个坑敲打

array.forEach(function(currentValue, index, arr), thisValue)

请看参数部分,第一个参数是个函数,函数的三个参数分别是当前值、索引和数组;第二个参数是函数所使用的this值,请看一下代码,区分两种的不同。

    var obj = {
        name: 'xx',
        times: [1, 2, 3],
        print: function() {//匿名函数的调用者是global/window
            var that = this//指向obj;
            this/*指向obj*/.times.forEach(function(n) {
                console.log(this/*原本指向window,被that绑定到obj,*/.name)
            }/*前面声明了,此处作为参数传入forEach函数*/)
        }
    };
    obj.print();//此时返回三个空,因为this指的是window

    var obj = {
        name: 'xx',
        times: [1, 2, 3],
        print: function() {
            var that = this//指向obj;
            this/*指向obj*/.times.forEach(function(n) {
                console.log(this/*原本指向window,被that绑定到obj,*/.name)
            },that/*前面声明了,此处作为参数传入forEach函数*/)
        }
    };
    obj.print();//三个xx

匿名函数的this指向的是全局,于是出现了你不指定this就会得到空的结果(因为全局里找不到name属性)。


③采用forEach方法

       Array.prototype.unique = function(){
            var n = [];
            this.forEach(function(element) {
                // if(n.indexOf(element) === -1){
                //     n.push(element);
                // }
                //includes用来判断一个数组是否包含一个指定的值
                if(!n.includes(element)){
                    n.push(element);
                }
            });
            return n;
        }

此处用到了includes()函数,用来判断一个数组里是否拥有某个元素。


④采用filter方法

       Array.prototype.unique = function() {
            //filter调用时使用参数 (element, index, array)
            //所以v=element,i=index
            return this.filter((v, i) => this.indexOf(v) === i);
            //返回一个新的通过测试的元素的集合的数组
        }

filter()函数需要注意的是他也和forEach一样不会改变原数组(map,some,every亦是如此)。


⑤采用indexOf方法

       Array.prototype.unique = function(){
            var n = [];//存放已遍历的满足条件的元素
            for(var i=0; i<this.length; i++){
                if(n.indexOf(this[i]) === -1){
                    n.push(this[i]);
                }
            }
            return n;
        }


⑥采用slice方法

        Array.prototype.unique = function(){
            for(var i=this.length; i>=0; i--){
                if(this.indexOf(this[i]) !== i){
                    this.splice(i,1);
                }
            }
            return this;
        }

最后这种方法记住是倒着走,就可以省去网上很多还要减1的麻烦啦吐舌头

猜你喜欢

转载自blog.csdn.net/weixin_40322503/article/details/79933490
今日推荐