JS 数组去重方法汇总
一、通过嵌套循环的方式
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
for (let i = 0, len = arr.length; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] === arr[j]) {
// 如果有重复,就从原数组中删去重复的元素,并修改 len 和 j 的值
arr.splice(j, 1);
len--;
j--;
}
}
}
return arr;
}
console.log(unique(arr));
// [1, '1', 2, null, undefined, NaN, NaN, {}, {}, /a/, /a/]
可以看到,这种方式对 NaN
和对象无效。
二、通过 indexOf 的方式
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
const result = [];
for (let i = 0, len = arr.length; i < len; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i])
}
}
return result;
}
console.log(unique(arr));
// [1, '1', 2, null, undefined, NaN, NaN, {}, {}, /a/, /a/]
可结合 filter 使用:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
// 每一项都从后一位置开始查找,如果没有相同的,则说明是重复项的最后一项,或只有这一项,通过过滤
const result = arr.filter((item, index) => arr.indexOf(item, index + 1) === -1);
return result;
}
console.log(unique(arr));
// [1, '1', 2, null, undefined, NaN, NaN, {}, {}, /a/, /a/]
可以看到,使用 indexOf 对 NaN
和对象无效。
三、通过排序的方式
将要去重的数组使用 sort
方法排序后,相同的值就会相邻,前后元素相同即可判断重复:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
const sortedArr = arr.sort();
const result = [sortedArr[0]];
for (let i = 1, len = sortedArr.length; i < len; i++) {
// 只要元素前后不一致的就是唯一的
if (sortedArr[i] !== sortedArr[i - 1]) {
result.push(sortedArr[i]);
}
}
return result;
}
console.log(unique(arr));
// [/a/, /a/, 1, '1', 2, NaN, NaN, {}, {}, null, undefined]
可结合 filter 使用:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
const sortedArr = arr.sort();
const result = sortedArr.filter((item, index) => {
return index === 0 || item !== sortedArr[index - 1]
})
return result;
}
console.log(unique(arr));
// [/a/, /a/, 1, '1', 2, NaN, NaN, {}, {}, null, undefined]
可以看到,用排序的方式对 NaN
和对象无效。
四、通过对象的方式
对象的属性值是唯一的,可以利用这个特性来去重,注意对象的键名是字符串,因此 obj[1]
与 obj['1']
的结果是一样的:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => {
const result = [];
const obj = {}; // 用来去重的对象
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
result.push(arr[i]);
obj[arr[i]] = 'x';
}
}
return result;
}
console.log(unique(arr));
// [1, 2, null, undefined, NaN, {}, /a/]
可以看到,通过对象的方式去重,对 NaN
和对象都有效。
五、通过 ES6 Set 的方式
ES6 Set 类似于数组,但其成员的值是唯一的,可利用这个特性实现数组去重,通过 Array.from 将 Set
结构转为数组结构:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => Array.from(new Set(arr))
console.log(unique(arr));
// [1, '1', 2, null, undefined, NaN, {}, {}, /a/, /a/]
可以用解构赋值简化:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
const unique = (arr) => [...new Set(arr)]
console.log(unique(arr));
// [1, '1', 2, null, undefined, NaN, {}, {}, /a/, /a/]
可以看到,通过 Set 去重数组对 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({} == {}); // false
console.log({} === {}); // false
console.log(/a/ == /a/); // false
console.log(/a/ === /a/); // false
对同个数组进行去重:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
上面各方式去重效果汇总如下:
去重方式 | 去重结果 | 结论 |
---|---|---|
嵌套循环 | [1, '1', 2, null, undefined, NaN, NaN, {}, {}, /a/, /a/] | NaN 和对象不去重 |
indexOf | [1, '1', 2, null, undefined, NaN, NaN, {}, {}, /a/, /a/] | NaN 和对象不去重 |
排序 | [/a/, /a/, 1, '1', 2, NaN, NaN, {}, {}, null, undefined] | NaN 和对象不去重 |
对象 | [1, 2, null, undefined, NaN, {}, /a/] | 全部去重 |
Set | [1, '1', 2, null, undefined, NaN, {}, {}, /a/, /a/] | 对象不去重 |
其中,只有对象的方式会对数字 1
和字符串 '1'
去重,且对其它任意类型也去重。
除此之外,lodash 也提供了 _.uniq(array) 方法来进行数组去重,效果如下:
const arr = [1, 1, 1, '1', '1', 2, 2, null, null, undefined, undefined, NaN, NaN, {}, {}, /a/, /a/];
console.log(_.uniq(arr));
// [1, '1', 2, null, undefined, NaN, {}, {}, /a/, /a/]
可以看到,对 NaN
有效,但对对象无效,与 Set
的效果一样。