前言
进阶第一篇,先用老生常谈,面试中常考的数组去重来练练手吧
循环套循环
比较暴力的解法
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){
//存在的则存入hash,
hash[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]
复制代码