JavaScript数组要领之数组去重要领

事情历程中经常会用到数组去重,用到的时刻每每一时想不到好要领,所以这里来总结一下去重要领。
运用es6去重代码很简朴,而且ES6已相称提高了。所以先来引见一下es6中的要领。

1.ES6中Map组织要领

function unique (arr) {
  const seen = new Map()
  return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}
let arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr));  //[ 1, 2, 3, '1', NaN, null, undefined, {}, {} ]

Map是es6中新增的数据组织,它类似于对象,也是键值对的鸠合,然则“键”的局限不限于字符串,各种范例的值(包含对象)都能够看成键。也就是说,Object 组织供应了“字符串—值”的对应,Map 组织供应了“值—值”的对应,是一种更完美的 Hash 组织完成。
Map的has要领用于推断map是不是含有该键。setget 要领分别为增加成员要领和获得键值要领。
上述要领一方面应用了map的has和set要领,一方面应用了数组的 filter要领,返回效果为真的元素构成的数组。
注重
Map 的键实际上是跟内存地址绑定的,只需内存地址不一样,就视为两个键。这句话不好明白的话,能够如许说假如 Map 的键是一个简朴范例的值(数字、字符串、布尔值),则只需两个值严厉相称,Map 将其视为一个键,比方0和-0就是一个键,布尔值true和字符串true则是两个差别的键, 对象是差别的键;别的,undefined和null也是两个差别的键。虽然NaN不严厉相称于自身,但 Map 将其视为统一个键

2. ES6中数组Set组织要领

function uniMap (arr) {
  return [...new Set(arr)];
}
let arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr));  // [ 1, 2, 3, '1', NaN, null, undefined, {}, {} ]

Set 也是ES6 供应的新的数据组织。它类似于数组,然则成员的值都是唯一的,没有反复的值。
Set 自身是一个组织函数,用来天生Set数据组织,它也接收一个数组或具有iterator接口的数据组织作为参数初始化。上述代码中就是应用了这个特征来完成对数组的去重。
Set 具有add要领来增加某个值,返回set组织自身。因而,应用add要领也能够完成数组的去重。比方:

const s = new Set();
function uniMap(arr)  {
    arr.forEach( item => s.add(item));
    return [...s];  // [ 1, 2, 3, '1' ]
}
// main
let arr = [1, 2, 1, 3, '1', 2, 2, 2];
console.log(uniMap(arr));

Array.from也是ES6中的新要领能够将 Set 组织转为数组。这就引出第三种运用set的数组去重要领:

// set3
function uniMap(arr) {
    return Array.from(new Set(arr));
}
let arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr));  // [ 1, 2, 3, '1', NaN, null, undefined, {}, {} ]

注重
Set 内部推断两个值是不是差别,运用的算法叫做“Same-value-zero equality”,它类似于准确相称运算符(===),所以上述效果中 1 和 ‘1’ 以为是不相同的,都被保存下来,重要的区别是NaN即是自身,而准确相称运算符以为NaN不即是自身。别的,两个对象老是不相称的。

3. 基础两重轮回

function uniMap(arr) {
    let res = [];
    for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) {
        let j = 0, resLen = res.length;
        for(; j < resLen; j +=1) {
            if(arr[i] === res[j]) {
                break;
            }
        }
        if( j === resLen) {
            res.push(arr[i]);
        }
    }
    return res;  
}
// main
let arr = [1, 2, 1, 3, '1', 2, 2, 2];
console.log(uniMap(arr)); // [ 1, 2, 3, '1' ]
arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr)); //[ 1, 2, 3, '1', NaN, NaN, null, undefined, {}, {} ]

我们运用轮回嵌套,最外层轮回 array,内里轮回 res,假如 array[i] 的值跟 res[j] 的值相称,就跳出轮回,假如都不即是,申明元素是唯一的,这时刻 j 的值就会即是 res 的长度,依据这个特性举行推断,将值增加进 res。
这个是最基础的要领,然则第一次写还真犯了错,没法去重。代码是如许的:


function uniMap(arr) {
    let res = [];
    for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) {
        let j = 0, resLen = res.length;
        for(; j < resLen; j +=1) {
            if(arr[i] === res[j]) {
                break;
            }
            res.push(arr[i]);
        }
    }
    return res;   // []
}
// main
let arr = [1, 2, 1, 3, '1', 2, 2, 2];
console.log(uniMap(arr));

和上面代码比拟,就是res.push(arr[i]);放在了内轮回里,少了 j === resLen的推断,就获得了空数组。原因是 初始的时刻res.length = 0,不会进到内轮回,所以res一直为空。果真眼高手低啊~

4. indexOf要领优化两重轮回中的内部轮回

// 两重轮回2 
function uniMap(arr) {
    let res = [];
    for(let i = 0, arrLen = arr.length; i < arrLen; i += 1) {
        if( res.indexOf(arr[i]) === -1) {
            res.push(arr[i]);
        }
    }
    return res;    
}
// main
let arr = [1, 2, 1, 3, '1', 2, 2, 2];
console.log(uniMap(arr)); // [ 1, 2, 3, '1' ]
arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr)); // [ 1, 2, 3, '1', NaN, NaN, null, undefined, {}, {} ]

5. filter要领优化两重轮回中的外层轮回

// filter要领
function uniMap(arr) {
    let res = [];
    return res = arr.filter( (item , index) => {
        return arr.indexOf(item) === index;
    })   //[ 1, 2, 3, '1', null, undefined ]
}
// main
let arr = [1, 2, 1, 3, '1', NaN, NaN, null, null, undefined, undefined, {}, {}];
console.log(uniMap(arr)); // [ 1, 2, 3, '1', null, undefined, {}, {} ]

此处的filter要领能够和要领3,4排列组合用~~实在要领1也应用了filter要领(filter人气高啊)filter要领道理已说过,遗忘的往上翻~

6. Object 要领

// object
function uniMap(arr) {
    let obj = {};
    return arr.filter( item => {
        return obj.hasOwnProperty(item) ? false : (obj[item] = true);
    })  
}
// main
let arr = [1, 2, 1, 3, '1', 2, 2, 2, NaN, NaN, null, null, undefined, undefined];
console.log(uniMap(arr)); // [ 1, 2, 3, NaN, null, undefined ]

上述代码道理是应用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比方 Object[value1] = true,在推断另一个值的时刻,假如 Object[value2]存在的话,就申明该值是反复的。从效果能够看到,他把数字 1 和字符串 ‘1’当成了统一个字符,由于对象的key值均是字符串,数字1被转换为字符串了,因而该要领适用于你想把数字和字符串去重的场所。

特别数据组织的去重推断

去重的要领就到此结束了,但是依据上面的效果能够看到,关于特别的数据范例比方:null、undefined、NaN、对象等,差别的去重要领实在效果是差别的。那末下面给个总结和剖析。
关于例子中的如许一个数组:
[1, 2, 1, 3, '1', 2, 2, 2, NaN, NaN, null, null, undefined, undefined];

要领效果申明
1.Map[ 1, 2, 3, ‘1’, NaN, null, undefined, {}, {} ]对象不去重
2.Set[ 1, 2, 3, ‘1’, NaN, null, undefined, {}, {} ]对象不去重
3.两重轮回[ 1, 2, 3, ‘1’, NaN, NaN, null, undefined, {}, {} ]对象和NaN都不去重
4.内层index[ 1, 2, 3, ‘1’, NaN, NaN, null, undefined, {}, {} ]对象和NaN不去重
5.外层filter[ 1, 2, 3, ‘1’, null, undefined, {}, {} ]对象不去重NaN被疏忽掉
6.Object要领[ 1, 2, 3, NaN, null, undefined ]数字和字符串去重,对象被疏忽

之所以涌现上面的效果,先看一下几个推断:

console.log(null === null);  // true
console.log(undefined === undefined); // true
console.log(NaN === NaN);  // false
console.log({} === {});  // false

再连系 indexOf 是运用 === 推断,以及set map 也运用 === 推断然则以为 NaN 和 NaN 相称,便能够剖析出来。

注重
关于数组元素和去重不是上述范例和效果的,那末针对你想要的去重去天真修正代码,不能够生搬硬套~~

    原文作者:侯贝贝
    原文地址: https://segmentfault.com/a/1190000013660675
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞