数组去重
# 数组去重
var arr = ['true','true',true,true,0,0,1,1,15,15,false,false,undefined,undefined,null,null,NaN,NaN,'NaN','NaN','a','a',{},{},{a:2},{a:2}];
# Set()
不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”
对象,后面的高阶方法会添加去掉重复“{}”
的方法。
function unique (arr) {
return Array.from(new Set(arr))
}
// or
[...new Set(arr)]
// ["true", true, 0, 1, 15, false, undefined, null, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}] // {} 没有去重
2
3
4
5
6
7
# Map()
function unique(arr) {
const newArray = [];
const tmp = new Map();
for(let i = 0; i < arr.length; i++){
if(!tmp.get(arr[i])){
tmp.set(arr[i], 1);
newArray.push(arr[i]);
}
}
return newArray;
}
function unique(arr) {
const tmp = new Map();
return arr.filter(item => !tmp.has(item) && tmp.set(item, 1) )
}
// ["true", true, 0, 1, 15, false, undefined, null, NaN, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}] // {} NaN 没有去重
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# splice()
双层循环,外层循环元素,内层循环时比较值。值相同时,删除元素,会改变原数组。
function unique(arr){
for(let i = 0; i < arr.length; i++){
for(let j = i + 1; j < arr.length; j++){
if(arr[i] === arr[j]){
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
// ["true", true, 0, 1, 15, false, undefined, null, NaN, NaN, "NaN", "a", {}, {}, {a:2}, {a:2}] // {} 没有去重
2
3
4
5
6
7
8
9
10
11
12
# sort() 相邻比对
利用sort()排序方法,先对原数组进行排序,然后再相邻元素比对
。
function unique(arr){
let array = arr.sort();
let newArr = [];
for(let i = 0; i < array.length; i++){
if(array[i] !== array[i + 1]){
newArr.push(array[i]);
}
}
return newArr;
}
// [0, 1, 15, "NaN", NaN, NaN, {}, {}, {a:2}, {a:2}, "a", false, null, true, "true", undefined] // NaN、{}没有去重
2
3
4
5
6
7
8
9
10
11
# indexOf()
// indexOf,返回数组的第一个值,所以使用filter过滤
function unique(arr) {
return arr.filter((item, index) => arr.indexOf(item) === index);
}
function unique(arr) {
const newArray = [];
arr.forEach(item => {
if (newArray.indexOf(item) === -1) newArray.push(item);
});
return newArray;
}
// [0, 1, 15, "NaN", NaN, NaN, {}, {}, {a:2}, {a:2}, "a", false, null, true, "true", undefined] // NaN、{}没有去重
2
3
4
5
6
7
8
9
10
11
12
13
14
# forEach + includes完美版(ok)
function unique (arr) {
let newArr = [];
let obj = {};
arr.forEach(item => {
if (typeof item !== 'object') {
if (!newArr.includes(item)) {
newArr.push(item)
}
} else {
// 对象
let str = JSON.stringify(item)
if (!obj[str]) {
newArr.push(item)
obj[str] = 1
}
}
})
return newArr
}
// [0, 1, 15, "NaN", NaN, NaN, {}, {a:2}, "a", false, null, true, "true", undefined]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 另一种优雅的去重方法
从给数组中的对象去重看Javascript中的reduce() (opens new window)
假设有这样一个数组:
let person = [
{id: 0, name: "小明"},
{id: 1, name: "小张"},
{id: 2, name: "小李"},
{id: 3, name: "小孙"},
{id: 1, name: "小周"},
{id: 2, name: "小陈"},
]
2
3
4
5
6
7
8
我们想去掉数组中id重复的对象,比如同样id为2的两个对象——
{id: 2, name: "小李"}和{id: 2, name: "小陈"} (去掉任何一个都可以) 我们该如何去做呢?
事实上,对于数组对象,传统的去重方法无能为力,至于forEach()、filter()等迭代方法也不好使;真正能做到优雅去重的,是ES5新增加的一个方法——reduce()
reduce()方法接收一个回调函数作为第一个参数,回调函数又接受四个参数,分别是:
- previousValue => 初始值或上一次回调函数叠加的值;
- currentValue => 本次回调(循环)将要执行的值;
- index =>“currentValue”的索引值;
- arr => 数组本身;
reduce()方法返回的是最后一次调用回调函数的返回值;
let log = console.log.bind(console);
let arr = [1,2,3,4,5,6];
arr = arr.reduce((previousValue, currentValue) => {
return previousValue + currentValue; //返回的是最后一次调用回调函数的值,15+6;
})
log(arr); // 21
2
3
4
5
6
可以看出,上面代码的最终结果就是1+2+3+4+5+6 = 21;
此外,reduce还可以接收第二参数initialValue,用来声明回调函数(第一个参数)的previousValue的类型和初始值;
let log = console.log.bind(console);
let arr = [1,2,3,4,5,6];
arr = arr.reduce((previousValue,currentValue) => {
return previousValue + currentValue;
},0) //指定cur的类型为Number并且初始值为0,当设为1时,最终打印的值为22
log(arr); // 21
2
3
4
5
6
需要注意的是,如果设置了initialValue的值,第一次执行回调函数的previousValue的值等于initialValue,此时查看当前索引(index)为0;但如果不设置initialValue的值,previousValue的值为数组的第一项,并且索引值(index)为1;也就是说,不设置初始值的话reduce()方法实际是从第二次循环开始的!
现在让我们回到文章开头的那个数组:
let log = console.log.bind(console);
let person = [
{id: 0, name: "小明"},
{id: 1, name: "小张"},
{id: 2, name: "小李"},
{id: 3, name: "小孙"},
{id: 1, name: "小周"},
{id: 2, name: "小陈"},
];
let obj = {};
person = person.reduce((cur,next) => {
if(!obj[next.id]) obj[next.id] = true && cur.push(next);
return cur;
},[]) //设置cur默认类型为数组,并且初始值为空的数组
log(person);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
打印person后,我们就可以得到去重后的数组。
当然, redecu()除了累加和去重外,功能还有很多,比如可以扁平化多维数组——
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
return a.concat(b);
}, []); // [0,1,2,3,4,5]
2
3
再说句题外的,提到去重,很多人都会想到ES6的Set;不过根据实验,Set还是适合对基本类型的去重,如果Set中的每一项是对象的话,是不会去重的,j即使有的对象一模一样——
let arr = new Set([
{id: 0, name: "小明"},
{id: 0, name: "小明"},
{id: 0, name: "小明"},
{id: 0, name: "小明"}
]);
console.log([...arr]); //依旧是这4个对象
2
3
4
5
6
7