文章目录
- 📋前言
- 🎯什么是数组去重,运用场景是什么?
- 🎯常用的数组去重方法
- 🧩使用 Set 对象
- 🧩使用 Object(对象、基于Hash哈希表) 或 Map
- 🧩使用 filter 方法与 indexOf 方法
- 🎯其他方法
- 🧩使用 reduce 方法
- 🧩使用递归方法
- 🧩使用 ES6 中的 Symbol 数据类型
- 🧩使用 hasOwnProperty 方法
- 🧩使用 lodash 库
- 📝最后
📋前言
好久没写面试题的文章了,今天这篇文章讲一讲一个常见的面试题。在前端开发岗位的面试过程中,我们可能或多或少会提及数组这个知识点,对于数组的相关操作也是一个经常提及的技术点,其中数组去重
是面试中非常常见的一个问题,无论是手写还是口述,我们都要有清晰的逻辑和思路去面对这个问题,因此这篇文章我们来浅理解在JavaScript中如何实现数组去重
,让你在面试无压力回答面试官。
🎯什么是数组去重,运用场景是什么?
数组去重
就是将一个数组中重复的元素去掉,只保留一个或不保留。在实际开发中,数组去重
非常重要,它可以提高代码执行效率,减少内存开销,并且使数据更加规范化和易于处理。这个过程可以使用多种方法来实现,具体取决于需求和场景。
在实际开发中,数组去重
常常用于以下场景:
- 数据展示:
- 在一些数据展示的场景中,我们通常需要从后台获取到一个数组类型的数据,并将其渲染到页面上。如果数组中存在重复项,则会影响数据的展示效果。因此,我们通常需要在渲染之前使用数组去重方法对数据进行处理,以提高数据展示的质量和效率。
- 数据统计:
- 在进行数据统计时,通常需要对数组中的元素进行去重操作,以减少重复计算,提高计算效率。例如,在进行 UV 统计时,我们需要对访客 IP 进行去重操作,以得到准确的 UV 计数结果。
- ❗补充:UV 是 Unique Visitor(独立访客)的简称,指访问某个网站或页面的不同访客数量。UV 统计是对网站访问情况进行统计的一种方法,用于分析网站流量、用户行为等数据。
- 表单验证:
- 在表单验证过程中,我们需要对用户输入的内容进行校验,避免用户重复提交相同的内容,这时候我们就需要对表单数据进行去重操作,以提高表单验证的准确性和稳定性。
🎯常用的数组去重方法
在实际开发中,为了提高代码执行效率和减少内存开销,我们通常采用以下几种数组去重
方法。
🧩使用 Set 对象
Set
是 ES6 新增的内置对象,可以用来存储无重复值的集合。我们可以将数组转换为 Set
,然后再将 Set
转换回数组即可。Set
对象的一个优点是可以快速地去除数组中的重复项。
const arr = [1, 2, 3, 3, 4, 5, 5];
const newArr = [...new Set(arr)];
console.log(newArr); // [1, 2, 3, 4, 5]
除了使用ES6中的扩展运算符加Set来实现数组去重,还可以利用 ES6 中的 from
方法。利用 ES6 中的 from
方法和 Set
数据结构,将数组转换成 Set
数据结构,再将 Set
数据结构转换回数组。
const arr = [1, 2, 3, 3, 4, 5, 5];
const newArr = Array.from(new Set(arr));
console.log(newArr); // [1, 2, 3, 4, 5]
❗ 补充:
下面这段代码是实际开发中数组去重的一个案例,这段代码的作用是,把用户输入的新词条添加到历史记录的数组中,然后通过 Set
来对这个数组进行去重操作,确保渲染出来的搜索历史记录没有重复显示。
🧩使用 Object(对象、基于Hash哈希表) 或 Map
利用 Map
数据结构,遍历数组,返回一个新的数组,该数组中只包含不与前面元素重复的元素。
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = [];
let map = new Map();
for(let i = 0; i < arr.length; i++){
if(!map.has(arr[i])){
map.set(arr[i], true);
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4, 5]
这种方法跟使用对象实现的去重方法相似,只不过用的是 ES6 中新增的 Map
数据结构。Map
对象保存键值对,并且能够记住键的原始插入顺序。所以通过遍历数组将每个元素作为键存储在 Map
中,如果该元素不存在,则说明它是新的元素,此时将它添加到新建的数组 newArr 中,并在 Map
中将其添加为键。最后输出 newArr 数组即可。
接下来看看使用对象实现的去重方法。
1️⃣基于 Hash 表实现,首先创建一个空对象,遍历数组,将数组元素作为对象的键值,如果该键不存在,则添加键值对,否则不做操作。最后将对象中的所有键转换成数组即可。
let arr = [1, 2, 3, 3, 4, 4, 5];
let obj = {};
for(let i = 0; i < arr.length; i++){
let value = arr[i];
obj[value] = true;
}
let newArr = Object.keys(obj).map(function(item){
return Number(item);
});
console.log(newArr); // [1, 2, 3, 4, 5]
它的原理是利用 JavaScript 中对象的属性唯一性
,将数组中的元素作为对象的属性名,把属性值都设为 true(或任意其他值),然后再利用 Object.keys
方法获取对象的所有属性名(即数组中的不同元素),最后再利用 map 方法
将属性名转换为数字类型即可。(需要注意的一点是,由于对象的属性名必须是字符串类型,因此在后续的操作中可以将它们转换为数字类型,但这并不影响数组去重的结果。)
2️⃣定义一个空对象 obj 和一个空数组 newArr。遍历原始数组 arr,对于每一个元素,执行以下操作: a. 判断 obj 中是否存在该元素,如果不存在,则将其加入 obj,并将其加入 newArr; b. 如果已经存在,则不做任何操作。遍历完所有元素后,newArr 中就存储了去重后的结果,即为 arr 去重之后的新数组。
const arr = [1, 2, 3, 3, 4, 5, 5];
const obj = {};
const newArr = [];
for(let i = 0; i < arr.length; i++) {
if(!obj[arr[i]]) {
obj[arr[i]] = true;
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4, 5]
📌总结:
- 基于 Hash 表的方法仅适用于数组中元素都是基本数据类型的情况,对于包含引用类型的数组,需要使用其他方法实现去重。此外,由于 obj 对象的存在,使用这种方法会占用额外的内存空间,因此在大规模数据处理时需要考虑性能问题。
- 相比较于基于 Hash 表的方法,使用 Map 的方法避免了对象 key 会被隐式转换成字符串的问题,同时也更加简洁明了。在大规模数据处理时,它的性能也有很好的表现。(需要注意的是,Map 是 ES6 中新增的数据结构,如果要在低版本浏览器中使用,需要进行兼容处理。)
🧩使用 filter 方法与 indexOf 方法
通过 filter
方法和 indexOf
方法来实现数组去重。对于数组中的每个元素,只保留第一个出现的元素,其他的重复元素通过 filter
方法过滤掉。
const arr = [1, 2, 3, 3, 4, 5, 5];
const newArr = arr.filter((item, index) => {
return arr.indexOf(item) === index;
});
console.log(newArr); // [1, 2, 3, 4, 5]
不使用 filter
的情况下,只使用 indexOf
方法进行去重,可以通过遍历原数组 arr,对于每个元素,判断它在数组中第一次出现的位置是否等于当前位置。如果相等,则说明它是第一次出现,将其添加到新建的数组 newArr 中即可。
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = [];
for(let i = 0; i < arr.length; i++){
if(arr.indexOf(arr[i]) === i){
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4, 5]
❗ 补充:与 indexOf
方法类似,可以用 includes
方法进行数组去重
的操作,逻辑思路也非常相似,区别在于这两个方法的实质区别。
includes()
方法是 ES7 引入的新方法,该方法判断一个数组是否包含一个指定的值,如果是,则返回 true;否则,返回 false。需要注意的是,includes()
方法是区分基本数据类型和引用数据类型的,也就是说,对于引用数据类型,它们的地址不同即使它们所存储的值相同,也会被认为是不同的元素。另外,includes()
方法不返回符合条件的元素的下标,而是直接返回一个布尔值。
indexOf()
方法是 JavaScript中常用的数组方法,用于返回指定元素在数组中第一次出现的位置。与 includes()
方法不同,indexOf()
方法返回符合条件的元素的下标位置,如果没有找到,则返回 -1。
includes()
方法只接受一个参数,即要查找的元素;而 indexOf()
方法可以接受两个参数,第一个参数为要查找的元素,第二个参数为起始搜索位置的索引值。
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = [];
for(let i = 0; i < arr.length; i++){
if(!newArr.includes(arr[i])){
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4, 5]
🎯其他方法
除了上述的这些较为常用的方法,数组去重
的方法还有很多,这里就不详细介绍了,接下来简单介绍几个。
🧩使用 reduce 方法
利用数组的 reduce
方法,遍历数组,使用一个变量保存上一个已经出现过的元素,并将当前元素与该变量进行比较,若不相同则加入新的数组中。
1️⃣使用indexOf()
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = arr.reduce((item, index) => {
if(item.indexOf(index)===-1){
item.push(index);
}
return item;
}, []);
console.log(newArr); // [1, 2, 3, 4, 5]
2️⃣使用includes()
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = arr.reduce((item, index) => {
if(!item.includes(index)){
item.push(index);
}
return item;
}, []);
console.log(newArr); // [1, 2, 3, 4, 5]
🧩使用递归方法
递归
地遍历数组并将出现在前面的元素和后面出现的相同元素删除掉。
let arr = [1, 2, 3, 3, 4, 4, 5];
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--;
}
}
}
console.log(arr); // [1, 2, 3, 4, 5]
🧩使用 ES6 中的 Symbol 数据类型
创建一个空对象,遍历数组,将数组元素作为对象的键值,由于 Symbol
值在对象属性名上是唯一的,因此如果该键不存在,则添加该键,否则不做操作。最后将所有键转换成数组即可。
let arr = [1, 2, 3, 3, 4, 4, 5];
let obj = {};
for(let i = 0; i < arr.length; i++){
let value = arr[i];
obj[Symbol.for(value)] = value;
}
let newArr = Object.getOwnPropertySymbols(obj).map(function(sym){
return obj[sym];
});
console.log(newArr); // [1, 2, 3, 4, 5]
🧩使用 hasOwnProperty 方法
利用 Object 对象的 hasOwnProperty
方法,利用其属性名唯一特性,遍历数组,返回一个新的数组,该数组中只包含不与前面元素重复的元素。
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = [];
let obj = {};
for(let i = 0; i < arr.length; i++){
if(!obj.hasOwnProperty(arr[i])){
obj[arr[i]] = true;
newArr.push(arr[i]);
}
}
console.log(newArr); // [1, 2, 3, 4, 5]
🧩使用 lodash 库
lodash
库是一个 JavaScript 的实用工具库,其中包含了大量的常用函数。使用 lodash
库的 uniq
函数即可实现数组去重。
import { uniq } from 'lodash';
let arr = [1, 2, 3, 3, 4, 4, 5];
let newArr = uniq(arr);
console.log(newArr); // [1, 2, 3, 4, 5]
📝最后
在开发中,数组去重
非常重要,它可以提高代码执行效率,减少内存开销,并且使数据更加规范化和易于处理。数组去重
的方法有很多,我们要具体问题具体分析,可以选择根据具体场景和需求,选用最适合的去重方法,多方面考虑,比如说考虑这个方法的时间复杂度、数组大小、数组是否要过滤、排序等等问题。