阅读 474

JavaScript进阶系列之数组去重(一)

前言

进阶第一篇,先用老生常谈,面试中常考的数组去重来练练手吧

循环套循环

比较暴力的解法

let array = [1,2,3,3,4,5,4]

function unique(array) {
    // res用来存储结果
    let res = []
    let lens = arrray.length
    let resLens = res.length
    for (let i = 0, i < lens; i++) {
        for (let j = 0,  j < resLens; j++ ) {
            if (array[i] === res[j]) {
                break
            }
        }
        // 如果array[i]是唯一的,那么执行完循环,j等于resLens
        if (j === resLens) {
            res.push(array[i])
        }
    }
    return res
}

console.log(unique(array)); // [1,2,3,4,5]
复制代码

在这个方法中,我们使用循环嵌套,最外层循环 array,里面循环 res,如果 array[i] 的值跟 res[j] 的值相等,就跳出循环,如果都不等于,说明元素是唯一的,这时候 j 的值就会等于 res 的长度,根据这个特点进行判断,将值添加进 res。

优点就是兼容性好。

Hash

Hash结构用来记录次数,比较好理解

let array = [1,2,3,3,4,5,4]
let unique = (arr) => {
    let hash = {}
    let res = []
    for (key in arr) {
    	//表内如果没有这个元素,就记录为1
        if (!hash[arr[key]]) {
            hash[arr[key]] = 1
            res.push(arr[key])
        }
    }
    return res
}
console.log(unique(array)); // [1,2,3,4,5]
复制代码

利用哈希表,如果 Hash 表中不存在则记录为1,最后形成一个{1:1,2:1,3:1,4:1,5:1}的结构,之后将 key 推入新数组中。

indexOf

利用 indexOf 查找代替循环

let array = [1,2,3,3,4,5,4]

function unique(array) {
    let res = []
    let len = array.length
    for (let i = 0,  i < len; i++) {
        let current = array[i]
        if (res.indexOf(current) === -1) {
            res.push(current)
        }
    }
    return res
}

console.log(unique(array)) // [1,2,3,4,5]
复制代码

利用 indexOf 查找元素第一次出现的位置的特性与 filter 的配合

let unique = function(){
    let arr = [...array]
    arr.filter((item,index,array)=>{
        return array.indexOf(item) === index
    })
    return arr
}
复制代码

复杂类型去重

利用 Hash 、 Reduce 与深拷贝

let array = [{value: 1}, {value: 1}, {value: 2}];

function unique(array) {
    let obj = {};
    return array.filter(function(item){
        return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? 
        false : (obj[typeof item + JSON.stringify(item)] = true)
    })
}
console.log(unique(array)); // [{value: 1}, {value: 2}]
复制代码

多维去重

参考 lodash 利用 reduce 和 hash 实现多维去重

let hash = {};

function uniqueBy(arr, key){
    return arr.reduce(function(previousValue, currentValue){
        //存在的则存入hashhash[currentValue[key]] ? '' : hash[currentValue[key]] = true && previousValue.push(currentValue);
        return previousValue
    }, []);
}
//去除name相同的对象
const uniqueArr = uniqueBy([{name: 'zs', age: 15}, {name: 'lisi'}, {name: 'zs'}], 'name');

console.log(uniqueArr); //[{name: 'zs', age: 15}, {name: 'lisi'}]
复制代码

ES6

利用 Set 数据结构不存在重复属性

let array = [1,2,3,3,4,5,4]
let unique = arr => [...new Set(arr)]
console.log(unique(array)); // [1,2,3,4,5]
复制代码

利用 Map 数据结构得到不存在 map 中并将其赋值 value 为 1 的值

let array = [1,2,3,3,4,5,4]
let unique = arr => {
    const map = new Map() 
    return arr.filter(item => !map.has(item) && map.set(item, 1))
}
console.log(unique(array)); // [1,2,3,4,5]
复制代码

特殊类型比较

去重的方法就到此结束了,然而要去重的元素类型可能是多种多样,除了例子中简单的 1 和 '1' 之外,其实还有 null、undefined、NaN、对象等,那么对于这些元素,之前的这些方法的去重结果又是怎样呢?

在此之前,先让我们先看几个例子:

var str1 = '1';
var str2 = new String('1');

console.log(str1 == str2); // true
console.log(str1 === str2); // false

console.log(null == null); // true
console.log(null === null); // true

console.log(undefined == undefined); // true
console.log(undefined === undefined); // true

console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

console.log(/a/ == /a/); // false
console.log(/a/ === /a/); // false

console.log({} == {}); // false
console.log({} === {}); // false

console.log([] == []); //false
console.log([] === []); //false
复制代码

那么,对于这样一个数组

var array = [1, 1, '1', '1', null, null, undefined, undefined, new String('1'), new String('1'), /a/, /a/, NaN, NaN,[],[]];
复制代码

以上各种方法去重的结果到底是什么样的呢?

我特地整理了一个列表,我们重点关注下对象和 NaN 的去重情况:

方法 结果 说明
for循环 [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] 对象、数组和 NaN 不去重
indexOf [1, "1", null, undefined, String, String, /a/, /a/, NaN, NaN] 对象、数组和 NaN 不去重
hash结构 [/a/, /a/, "1", 1, String, 1, String, NaN, NaN, null, undefined] 全部去重
filter + indexOf [1, "1", null, undefined, String, String, /a/, /a/] 对象、数组不去重, NaN 会被忽略掉
Set [1, "1", null, undefined, String, String, /a/, /a/, NaN] 对象不去重 NaN 去重

想了解为什么会出现以上的结果,看两个 demo 便能明白:

// demo1
var arr = [1, 2, NaN];
arr.indexOf(NaN); // -1
复制代码

indexOf 底层还是使用 === 进行判断,因为 NaN === NaN的结果为 false,所以使用 indexOf 查找不到 NaN 元素

// demo2
function unique(array) {
   return Array.from(new Set(array));
}
console.log(unique([NaN, NaN])) // [NaN]
复制代码

Set 认为尽管 NaN === NaN 为 false,但是这两个元素是重复的。

var unique = arr => [...new Set(arr)]
console.log(unique([NaN,NaN,null,null,null])) //[NaN,null]
复制代码
关注下面的标签,发现更多相似文章
评论