前端面试手写代码——JS数组去重
1 测试用例
// 测试用例
const a = {};
const b = { c: 1 };
const array = [
1, 1, "1", "1",
{}, {}, { c: 1 }, { c: 1},
a, a, b, b,
[], [], [1], [1],
undefined, undefined,
null, null,
NaN, NaN,
];
2 JS 数组去重4大类型 2.1 元素比较型 此类型通过数组元素之间进行比较来去重
2.1.1 双层 for 循环逐一比较(es5常用)
使用双层
for
循环逐一比较数组元素,用splice
方法去除重复的元素// 双层for循环
function uniq1(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
}// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null,NaN,NaN]
通过对比去重前后结果,重复的
NaN
没有去掉,因为NaN === NaN
为false
2.1.2 排序相邻比较
使用
sort()
方法对数组元素进行排序,然后比较相邻元素,用splice
方法去除重复的元素。function uni2(arr) {
arr.sort();
for (let i = 0;
i < arr.length - 1;
i++) {
arr[i] === arr[i + 1] && arr.splice(i + 1, 1) && i--;
}
return arr;
}
也可以创建新数组,将不重复的元素放入新数组中
function uniq3(arr) {
arr = arr.sort()
const newArr = [arr[0]]
for (let i = 1;
i < arr.length;
i++) {
if (arr[i] !== arr[i - 1]) {
newArr.push(arr[i])
}
}
return newArr
}// 去重结果
// [[],[],1,'1',[1],[1],NaN,NaN,{},{},{c:1},{c:1},{},{c:1},null,undefined]
- 重复的
NaN
没有去掉,因为NaN === NaN
为false
sort
默认排序顺序是将元素转换为字符串,对象转换为字符串都是[object Object]
,所以sort
方法不能对数组中的对象进行排序,也就有可能无法去除重复的对象,除非重复的对象本就相邻
2.2.1 indexOf
【前端面试手写代码——JS数组去重】通过
indexOf
查找当前元素第一次出现的位置是否为当前位置,若是,则放入新数组function uniq4(arr) {
let res = []
for (let i = 0;
i < arr.length;
i++) {
if (arr.indexOf(arr[i]) === i) {
res.push(arr[i])
}
}
return res
}// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null]
同样,因为
NaN === NaN
为false
,所以用indexOf
查找NaN
结果总是-1
,从而在新数组中不会有NaN
2.2.2 findIndex
通过
findIndex
查找当前元素第一次出现的位置是否为当前位置,若是,则放入新数组function uniq5(arr) {
let res = []
for (let i = 0;
i < arr.length;
i++) {
if (arr.findIndex(item => item === arr[i]) === i) {
res.push(arr[i])
}
}
return res
}
// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null]
同样,因为
NaN === NaN
为false
,所以用findIndex
查找NaN
结果总是-1
,从而在新数组中不会有NaN
2.3 元素是否存在型 此类型通过判断在新数组中是否存在当前元素来去重
2.3.1 includes
includes
方法用来判断一个数组是否包含一个指定的值function uniq6(arr) {
let res = []
for (let i = 0;
i < arr.length;
i++) {
if (!res.includes(arr[i])) {
res.push(arr[i])
}
}
return res
}
// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null,NaN]
includes
使用零值相等算法来确定是否找到给定的元素,所以可以判断NaN
是否在新数组中存在2.3.2 some
some
方法用来测试数组中是否至少有1个元素通过了被提供的函数测试function uniq7(arr) {
let res = []
for (let i = 0;
i < arr.length;
i++) {
if (!res.some(item => item === arr[i])) {
res.push(arr[i])
}
}
return res
}
// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null,NaN,NaN]
同样,这里仍旧使用了
===
来比较元素,因为NaN === NaN
为false
,所以新数组中会有多个NaN
2.4 依托数据结构特性 此类型通过
ES6
提供的数据结构Map
、Set
本身不可重复特性来去重2.4.1 Map
ES6
提供的Map
结构可以用各种类型的值(包括对象)当作键,且键是唯一的function uniq8(arr) {
const map = new Map()
for (let i = 0;
i < arr.length;
i++) {
!map.has(arr[i]) && map.set(arr[i], true)
}
return [...map.keys()]
}
// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null,NaN]
map.has
方法对NaN
也有效2.4.2 Set(ES6 最常用)
Set
结构的成员的值都是唯一的,没有重复的值。function uniq9(arr) {
return [...new Set(arr)]
}// 去重结果
// [1,'1',{},{},{c:1},{c:1},{},{c:1},[],[],[1],[1],undefined,null,NaN]
3 补充
- 上面所说的方法可以使用不同的
Api
进行改动,比如使用splice
方法去除数组元素的地方,我们可以通过filter
方法来过滤数组得到新数组;
再比如includes
的方法中不用for
循环遍历数组,通过reduce
方法来代替等等。
总之,方法有很多,但是万变不离其宗
- 有些去重方法对
NaN
无效,因为NaN === NaN
为false
,如果有需求,可以使用Object.is(NaN, NaN)
为true
来进行修改 - 实际应用中,最常用的方法就是使用
Set
,也可以使用第三方库lodash
来处理
公众号【前端嘛】获取更多优质内容
推荐阅读
- Jsr303做前端数据校验
- 2018国考外交部面试演讲不再难——只需把握好三点
- iOS面试题--基础
- 7、前端--jQuery简介、基本选择器、基本筛选器、属性选择器、表单选择器、筛选器方法、节点操作、绑定事件
- 前端代码|前端代码 返回顶部 backToTop
- java|java 常用知识点链接
- 前端|web前端dya07--ES6高级语法的转化&render&vue与webpack&export
- 前端自学笔记01
- js保留自定义小数点
- 年薪30万的Java架构师必会的springboot面试题