Skip to main content

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/]

可以看到,使用 indexOfNaN 和对象无效。

三、通过排序的方式

将要去重的数组使用 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.fromSet 结构转为数组结构:

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 的效果一样。