1.怎么解决一个数组去重问题
我看了很多面试相关的问题,发现数组去重出现过几次,这里就对数组去重问题进行一个总结。并且进行扩展。请一定要看到第二章!
问题描述:数组去重,顾名思义就是,把数组里的重复数值去除,使其多个相同的数值变为一个,最后使数组里不含有重复数值。
举个例子:有个数组:[1,2,3,4,4,5,5,5,6,7]
,数组去重后就变为[1,2,3,4,5,6,7]
。
该问题有很多解决方法,这里将一一列出,并且会对其个别重要知识点进行扩展
解法将由浅入深😏,一定要看到最后几个解法!
🤔 解法1:
使用双重for和splice
// 双重for加splice
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){
//第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
🤔 解法2
使用indexof
方法和新数组
//使用indexof
function unique(arr) {
var array = [];//用新数组来装
for (let i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {
//indexof返回-1表示在新数组中不存在该元素
array.push(arr[i])//是新数组里没有的元素就push入
}
}
return array;
}
使用includes
也可以判断是否含有某值
function unique(arr) {
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes(arr[i]) ) {
//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
indexOf()
方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。有两个参数,第一个参数是要查找的元素,第二个参数可选,是开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true
,否则返回false
。其也有两个参数,第一个是要查找的元素,第二个可选,是开始查找的位置,与indexof
相同的是,第二个参数为负值的话,就从末尾开始往前跳 参数 的绝对值个索引,然后往后搜寻。默认为 0
🤔 解法3
使用sort方法先排序,使相同的元素都相邻
function unique(arr) {
arr = arr.sort((a, b) => a - b)//sort先按从小到大排序
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
sort方法用于从小到大排序(返回一个新数组),其参数中不带以上回调函数就会在两位数及以上时出现排序错误(如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。两位数会变为长度为二的字符串来计算)。所以自己要写一个排序标准,当回调函数返回值大于0时两个值调换顺序。
🤔 解法4
ES6 提供了新的数据结构 Set。Set可以非常简单的去重
function unique(arr) {
const result=new Set(arr);
return [...result];
//使用扩展运算符将Set数据结构转为数组
}
Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。
🤔 解法5
使用Map
function unique(arr) {
let map = new Map();
let array = new Array(); // 数组用于返回结果
for (let i = 0; i < arr.length; i++) {
if(map.has(arr[i])) { // 如果有该key值
map.set(arr[i], true);
} else {
map.set(arr[i], false); // 如果没有该key值
array.push(arr[i]);
}
}
return array ;
}
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
Map.prototype.has(key)
返回一个布尔值,表示Map实例是否包含键对应的值。Map.prototype.set(key, value)
设置Map对象中键的值。返回该Map对象。
🤔 解法6
使用filter
function unique(arr) {
return arr.filter(function (item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
//不是那么就证明是重复项,就舍弃
return arr.indexOf(item) === index;
})
}
filter英文意思是筛选,filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。其回调函数包含三个参数(数组中当前正在处理的元素,在处理的元素在数组中的索引(可选),调用了 filter 的数组本身(可选))
🤔 解法7
使用reduce加includes
function unique(arr){
let result=arr.reduce((acc,cur)=>{
if(!acc.includes(cur)){
acc.push(cur);
}
return acc;
},[])//[]作为回调函数的第一个参数的初始值
return result
}
2.数组去重解法7引出关于reduce的另一道面试题
reduce的用处很多,很重要,这里补上上面对reducer的知识点解释。
reduce介绍
MDN中对其的描述是:reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。其能做的功能很多,通过回调函数实现。
reduce的第一个参数是个回调函数,其有四个参数(累加器,当前值,当前索引,原数组),后两个参数可选。第二个参数是回调函数的第一个参数累加器的初始值(很重要)
注意:不给初始值,那么初始值就是原数组的第一个元素,计算从第二个元素开始。给了初始值就是从第一个元素开始
。
通过代码实现功能了解reduce与其回调函数的书写:
- 为存储数值的数值进行累加求和
let result=[1,2,3,4].reduce((acc,cur)=>{
return acc+cur;//acc为累加器(初始值为数组第一个元素),cur为当前元素
})
console.log(result);//输出10
//因为没初始值,所以从数组第二个元素开始计算,所以处理上面数组,回调函数共运行了3次
- 累加求和时给初始值
let result=[1,2,3,4].reduce((acc,cur,index,o)=>{
return acc+cur;
},10)
console.log(result);//输出20
//因为有初始值,所以从数组第一个元素开始计算,所以处理上面数组,回调函数共运行了4次
- 按属性对object进行分类
const bills=[{type:'transfer',momey:233},
{type:'study',momey:341},
{type:'shop',momey:821},
{type:'transfer',money:821},
{type:'study',momey:821}
]
let result=bills.reduce((acc,cur)=>{
if(!acc[cur.type]){//遇到不存在的类型,就新建一个空数组来装
acc[cur.type]=[];//二维数组
}
acc[cur.type].push(cur)
return acc;
},[])//为累加器设置初始值为空数组,作为分类用的容器
console.log(result);
//输出
[
transfer: [{ type: 'transfer', momey: 233 },{ type: 'transfer', money: 821}],
study: [ { type: 'study', momey: 341 }, { type: 'study', momey: 821 } ],
shop: [ { type: 'shop', momey: 821 } ]
]
reduce相关面试题
🤔 题目描述:请使用原生 JavaScript 实现一个方法,判断 html 中出现次数最多的标签,并统计这个次数。
知识点细化:
- 获取所有标签:
document.querySelector(*)
:列出页面内所有标签,*
表示选择器*
,也就是全部。 Object.entries()
:Object.entries()
返回一个数组
,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。简单的来说就是可以把对象的每个属性变为一个数组,这个数组里有两个值,一个为属性名,一个为属性值。例如:Object.entires({a:1,b:2,c:3})会得到[ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]
思路分析:<br/>
1. 先获得含有所有标签的NodeList数组,然后将其加工为只有标签名的数组,接着使用reduce得到一个对象,以标签名为属性名,标签数量为属性值的对象。<br/>
2.将上一步得到的对象用Object.entires()变为个二维数组,再使用reduce对其处理,得到数量最多的那个标签(``比较每个数组的tags[1],返回数组的tags[0]``)。
答案代码:
window.onload=function(){
// 最大数的思路是JS 必考的 使用reduce
const maxBy=function(list,tag){
return list.reduce(
function(x,y){
//根据reduce方法获得数量最大的那个标签
return tag(x)>tag(y)?x:y
}
)
}
function getFrequentTag(){
//得到reduce 需要的数组
const tags=[...document.querySelectorAll('*')].map(x=>x.tagName).reduce((acc,tag)=>{
acc[tag]=acc[tag]?acc[tag]+1:1;
//数组存在该元素,就值+1,否则创建元素,设置值为1
return acc;//得到以tag名为属性名,数量为属性值的对象
},{})//初始值为对象
return maxBy(Object.entries(tags),tag=>tag[1])
//tag=>tag[1]这个函数表示return数组的第二个值,也就是标签的数量
}
console.log(getFrequentTag());
}
扩展:如何得到第二多的标签,以及数量第X大的标签呢?
对上面的解法进行进一步的优化,我们要做的不在是得到最大,而是要得到任意大的标签!<br/>使用sort方法改进
代码:
//获得第X多的标签
window.onload=function(){
// 最大数的思路是JS 必考的 使用reduce
const maxByx=function(list,tag,x){
list=list.sort((a,b)=>{
return tag(b)-tag(a);//数量从大到小排序
})
return list[x];
}
function getFrequentTag(){
//得到reduce 需要的数组
const tags=[...document.querySelectorAll('*')].map(x=>x.tagName).reduce((acc,tag)=>{
acc[tag]=acc[tag]?acc[tag]+1:1;//数组存在该元素,就值+1,否则创建元素,设置值为1
return acc;//得到以tag名为属性名,数量为属性值的对象
},{})//初始值为对象
return maxByx(Object.entries(tags),tag=>tag[1],1)
//第三个参数用于指定要第几大的,这里指定第二大的
//tag=>tag[1]这个函数表示return数组的第二个值,也就是标签的数量
}
console.log(getFrequentTag());
}
根据上面代码return maxByx(Object.entries(tags),tag=>tag[1],1)
,我们只需要指定第三个参数值就能够得到我们要的数量排名第X的的那个标签。<br/>
这里就完成了面试中对得到html中最大数量标签的升华了!
感谢阅读,有好的建议请一定提出,感谢感谢,笔者最近也在准备面试中!
参考文章:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。