js-08 数组
一、数组常用方法
添加与删除 splice
从前添加或删除单个:
unshift
、shift
unshift()
;从前添加多个,返回数组的长度,改变数组shift()
;没有参数,删除第一项,返回删除值,从后添加或删除单个:
push
、pop
push()
;从后添加多个,返回数组长度,改变数组;pop()
;不写参数,从后删除一项,返回被删除的值。arr.push(arr.shift())
; 删除第一项并添加到最后一项;复合写法:
splice(n[,m][,add])
一个参数:删除n个;
两个参数:从n删除m个;
三个以上:从n删除m个,并开始添加;
改变原数组,返回被删除的项组成的数组,如果没有被删除的项,返回一个空数组;
注意:如果第一个为负数,表示从后面截取多少个,第二个参数则无效,第三个参数为添加;
如果是负数,是添加的数组的length,因此是从后面截取;
排序 sort
变量.sort()
;默认是以字符串的Unicode码一位一位比较的;
function(a,b){return a-b}
从小到大或从大到小,字符串不按照一位一位比较,返回排序完的数组,原数组改变。function(a,b){return a.localeCompare(b)}
字符串从小到大或从大到小。function(a,b){return a.localeCompare(b,"zh")}
字符串包含汉字以26字母从小到大或从大到小排列。sort还可以对对象排序
乱序:
arr.sort(function(){return Math.random()-0.5})
;理解:正序是大于1,倒序是小于1,乱序则在中间;
当数组对象的时候,这样使用;
var ary3=[
{name:"战三",age:20},
{name:"李四",age:18},
{name:"王五",age:28}
]
ary3.sort(function(a,b){
return a.name.localeCompare(b.name,"zh")
})
拼接 join
arr.join()
把数组变成字符串,参数就是拼接符;
将数组逗号连接字符串,如果括号为引号则拼接;可以指定字符串拼接;
参数也可以是标签,返回设置好的变量,原数组不会改变。
小练习:查找文字
var h = p.innerHTML;
btn.onclick = function(){
var val = input.value; //获取value值
var ary = h.split(val); //以搜索框的文字来分割成数组
var str = ary.join('<mark>' + val + '</mark>'); //join将分隔好的数组组成字符串;
p.innerHTML = str; //把组好的字符串返回文本标签;
}
翻转 reverse
arr.reverse()
数组倒过来排序,不用传参数,返回值倒过来的数组,原数组变为倒过来的;
合并 concat
arr1.concat(arr2)
将多个数组拼接起来,参数要拼接的数组,多个用逗号隔开,
参数可以是数组,也可以是单个项,
返回值,拼接后的数组,原数组不改变;
ES6的语法:[...arr1,...arr2]
,不用使用concat()
;
查找 indexOf
arr.indexOf(str)
查找,参数要查找的值,返回值有则返回下标,没有返回-1,原数组不会改变;,
查找的值要全等,IE8及以下不支持;
截取 slice
arr.slice(n, m)
和字符串用法一样;
不填参数,返回全部;
只有n时,从n开始截取到结束;,返回被截取的字符;
n,m时,从n截取到m,不包括m;
m大于n时,不置换下标,返回空字符串,不报错;
当有值为负数时,负数加字符串长度,返回参数,加完还是负数,从0开始;
也可以使用arr.length = 10
,将20个对象的数组只留前面10个;
总结:
会改变原数组的:push
,pop
,unshift
,shift
,splice
,sort
,reverse
,fill
,copyWithin
其他的方法都不会改变原数组;
实现a克隆b能想到几种方法
for
循环,slice
截取不传参数,concat
利用空数组拼接a;
二、数组迭代方法
下面所有的项都可以用for循环办到,只是将for循环包装了一下;
参数:function(数据,下标,数组本身){比较表达式}
。
全部 every
比较数组,返回布尔值,全真为真,一假全假。
返回值:true/false
;
不会改变原数组;
vue中实例:常用于在全选上面;
this.checkedAll = this.list.every(items=>items.flag)
//当全部选中时,则改变checkdAll复选框的状态;
一些 some
比较数组,返回布尔值,一真为真,全假为假。
返回值:true/false
;不会改变原数组;
过滤 filter
过滤,满足条件的组成新数组并返回。
ary.filter(function(value){
value>3 将大于3的数组成一个新数组,并返回;
});
改变 map
改变数组中的数据,返回改变的数组
定义:对数组的每一项都运行给定函数,返回每次函数调用的结果组成的数组;
比如:变量.map((value,index,ary){return value*3})
;让每一个value都*3并返回新数组;
map可以直接修改引用类型的数组(数组套对象),不需要返回;
// 第三个参数的使用方法
let a = [1,2,3,4,5,6]
let b = a.map((e,i,arr)=>{
return `${e}${i}${arr.find(e=>e%5===1)}`
})
//map修改数组对象优雅的方式
arr.map(item=>{
return {
...item,
obj: JSON.parse(item.obj) //使用对象解构的方式,之后再替换里面的对象;
}
})
循环 forEach
循环数组,参数是一样的,没有返回值,不需要return,原数组不改变;和for循环使用方法一样,
循环元素时第一个形参是每一个元素;
参数funtion(数据,下标,数组本身)
;,IE8以下不兼容;
只能循环NodeList数组:不能循环HTMLCollection类数组,对于HTMLCollection的数组,可以使用Array.from进行转换
var ary = [1,2,4,5,6,7]
var str = '';
var f = ary.forEach(function(value){
str+=value;
})
console.log(str); //返回是1234567;
注意:
使用迭代数组无法终止数组循环,不能break和containue,解决办法如下:
使用try代码块,在需要中断的地方抛出异常
jslet sum = 0 try{ Array.from({length:100}).map((_,i)=>i).forEach(item=>{ sum+=1 if(item === 50) throw Error() }) } catch (e){}
使用every、some来代替forEach循环解决,every可以return false来终止循环,some可以return true来终止循环,强烈推荐使用some,简单方便;
jslet sum = 0; let arr = Array.from({length:100}).map((_,i)=>i) // every写法 arr.every(item=>{ sum+=1 if(item > 50){ return false } return true }) console.log(sum) // 52 // some写法 arr.some(item=>{ sum+=1 if(item > 50) return false // some不需要在后面返回true了 })
三、数组扩展
Array.from(v)
: 将伪数组对象或可遍历对象转换为真数组,需要一个变量来接收;Array.of(v1, v2, v3)
: 将一系列值转换成数组,和new Array传值是一样的;
var arr = Array.of(3) // 这个3不是数组的长度,是一个数组
find()
;find返回符合条件的元素,之后的值不会再调用执行函数,如果找不到返回undefined,不会改变原数组;
arr.find((item,index,arr)=>{})
findIndex
:返回符合条件的元素的索引位置,没有返回-1,不改变原数组;
findIndex(item,index,arr=>{})
includes()
:判断数组是否包含否个值fill()
如果括号不填参,则将数组的每一项都返回undefined;
括号共有三个参数,第一个要置换的内容(必填),第二个从什么位置开始,第三个停止置换的前一个位置;
flat()
将一个多维数组深度转成一维(扁平化或称作降维);
常用于扁平化数组,括号填写扁平化多少层,填写Infinity,扁平所有;
flat方法会移除数组中的空项,[1,2,,4,5],会将2和4中间的移除;
var arr = [1,2,[1,2,[1,2,3,[1,2,]]]]
arr.flat(1) // 遍历一组
flatMap()
相当于flat和map的组合,在运算后直接将数组扁平化处理jslet a = [1,2,[3],[4,5]] let b = a.flatMap(e=>e+1) let c = a.map(e=>e+1).flat() console.log(b) // [2,3,'31','4,51'] console.log(c) // 如上
copyWithin
能复制数组中的某些元素,并将它放到同一个数组指定的位置;
三个参数:第一个要置换的位置(必填),第二个从什么位置开始复制,第三个要停止复制的前一个位置;
valueOf()
返回原数组,可以用来浅拷贝;keys()
返回数组的下标;会生成iterator接口,使用for..of取值或者Array.from;
四、reduce
arr.reduce(callback,[init])
第一个回调函数,回调函数必须有返回值,,一般返回第一个prev的值;
回调函数的四个参数:上一次回调函数的结果,当前值,项的索引,数组对象;
第二个参数为可选值,用于初始值,放到callback里进行计算;(可以放一个空数组,之后可以使用第一个值concat)
reduceRight
从后边开始循环;
理解:
let arr = [1,2,3,4,5]
let arr2 = arr.reduce((p,c)=>{
return p
},[])
// 第二个参数作为p第一次循环的值,如果没有则取数组中第一项,则c从第二项开始;
// p为上一次回调函数的结果,如果没有return p,则没有结果,则reduce没有意义;
// 上述代码中,p没有和c进行交互,p一直是[],则arr2结果为[];
简单使用
var arr = [1,2,3,4,5]
var sum = arr.reduce((x,y)=>x+y) //15
var mul = arr.reduce((x,y)=>x*y) //
数组去重案例
let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
if(!pre.includes(cur)){
return pre.concat(cur)
}else{
return pre
}
},[]) // 当有第二个参数时,第一次先循环一个空数组,第二次循环添加了一个的数组
console.log(newArr);// [1, 2, 3, 4]
对象案例
仅演示使用方法,案例不在
arr.reduce((pre,curr)=>{
if(curr.length === maxLen){
pre[curr[0]] = curr.length; // 将pre对象的键名,等于键值,这里的curr[0]为第一个;
}
return pre // 返回的是上一次回调函数的结果,最终会把最后一个结果返回出去;
},{})
修改对象的值(常用)
var obj = {
name: 'hhh',
age: '18'
}
var obj2 = Object.keys(obj).reduce((o, c) => {
o[c] = 18 + obj[c]
return o
}, {})
console.log(obj2) // {name: '18hhh', age: '1818'}
reduce代替map
let arr = [0,1,2,3,4,5]
arr.map(item=>item*2)
arr.reduce((p,c)=>{
p.push(c*2)
return p
},[])
reduce代替filter
let arr = [0,1,2,3,4]
arr.filter(item=>item<3)
arr.reduce((p,c)=>{
c < 3 && p.push(c)
return p
},[])
补充
将类数组转换为真正的数组
该函数可以调用任意数量的形参;
Array.prototype.slice.call(arguments)
;[].slice.call(arguments)
Array.from(arguments)
function addOne(){
return Array.prototype.slice.call(arguments)
}
console.log(addOne(1,2,3,4,5))
添加指定长度的空数组
Array.apply(null,{length:20})
会加入20个空的数组,20个长度的数组;
可以简写:Array.apply(null,Array(20))
;
es6写法:Array.from({length:20})或Array(20).fill()
; // fill里面也可以填写内容,以什么来填充;
Array.apply(null,{length:20}).map((item,index)=>{
console.log(index) // 打印20次
})
修改原数组代替concat
Array.prototype.push.apply(arr1,arr2)
由于concat不能直接修改原数组,可以通过此方法将arr2合并到arr1身上,apply第二个参数接受数组;
缺点:如果第二个数组太大时,不建议使用此方法来进行合并,因为一个函数能够接收的参数个数是有限的;
解决方法:将参数数组切块后循环传入目标
function concatOfArray (arr1, arr2) {
var QUANTUM = 32768;
for (var i = 0, len = arr2.length; i < len; i += QUANTUM) {
Array.prototype.push.apply(
arr1,
arr2.slice(i, Math.min(i + QUANTUM, len))
);
}
return arr1;
}
// 验证:
var arr1 = [1,2,3,4]
var arr2 = []
for(var i=0;i<1000000;i++){arr2.push(i)} //循环添加多个i
Array.prototype.push.apply(arr1,arr2) //爆栈
concatOfArray(arr1,arr2) //正常输出;
测试随机数取哪些数
如果需要测试随机数都取到了哪些数,通过创建空数组方法,添加到数组里面去;
Array.from({length:20}).map(i=>Math.ceil(Math.random()*6)))
随机验证码
Math.random().toString(36).substr(2,6)
高频面试题
数组去重
判断问题:拿第一个和每一个去做比较,所以需要用到两个循环;
- 写两个循环,第一个循环数组第一个数据,第二个循环让每一个和第一个做比较。
- 第二循环j=i+1,再判断第二个循环的下标是否和第一个循环的下标的字符相同,如果是相同的,则删除一个
- 删除之后数组变短,让j减一,重新做比较。
for(var i=0;i<ary.length;i++){
for(var k=i+1;k<ary.length;k++){
if(ary[i] === ary[k]){
if(i == k){ //如果k等于0,则需要这一步
continue;
}
ary.splice(k,1);
k--; //前面删除一个之后数组的长度会进1,此时的k会直接跳到循环去加1,让k减1;
}
}
}
有序数组去重
function removeDuplicates(nums) {
for(let i=0;i<nums.length;i++) {
nums.splice(i, nums.lastIndexOf(nums[i]) - i);
}
}
// or
var removeDuplicates = function (nums) {
if(!nums.length || !nums) return 0;
let left = 0;
for(let right=1;right<nums.length;right++) {
if(nums[left] !== nums[right]) {
nums[++left] = nums[right]
}
}
return ++left;
};
选择数组排序
判断问题:拿第一个和每一个去做比较,如果大于第二个,则让第一个到第二个位置去;
(1)写两个循环,第二个循环j=i+1;,不比较本身。
(2)判断数组的第一个字符i是否大于数组的第二个j字符,如果大于第一个字符,则调换位置。
冒泡数组排序
跟自己紧挨着的做比较,j>j+1,判断大于调换位置。
for(var i=0;i<ary.length;i++){
for(var k=0;k<ary.length;k++){
if(ary[k] > ary[k+1]){ //2k > 1n 1n 2k
var n=ary[k];
ary[k]=ary[k+1];
ary[k+1]=n;
} //自身和紧挨着的后一个比较;
}
}
其他
● 数组方法 pop() push() unshift() shift() ?
● split() join() 的区别?
● 编写一个数组去重的方法。
● [“1”, “2”, “3”].map(parseInt) 答案是多少?
● 冒泡算法排序?