数组方法
一、添加、移除数组元素
splice(修改原数组)
作用:添加、删除和插入元素
语法:
它从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, …, elemN。最后返回被删除的元素所组成的数组。
arr.splice(start[, deleteCount, elem1, …, elemN])
删除:(注:当只填写了 splice 的 start 参数时,将删除从索引 start 开始的所有数组项)
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1); // 从索引 1 开始删除 1 个元素
alert( arr ); // ["I", "JavaScript"]
删除并添加元素:
let arr = ["I", "study", "JavaScript", "right", "now"];
// 删除数组的前三项,并使用其他内容代替它们
let removed=arr.splice(0, 3, "Let's", "dance");
alert( removed ); // "I", "study" ,"JavaScript" <-- 被从数组中删除了的元素
alert( arr ) // 现在 ["Let's", "dance", "right", "now"]
插入元素而不删除任何元素:(deleteCount 设置为 0)
let arr = ["I", "study", "JavaScript"];
// 从索引 2 开始
// 删除 0 个元素
// 然后往索引2的前方插入 "complex" 和 "language"
arr.splice(2, 0, "complex", "language");
alert( arr ); // "I", "study", "complex", "language", "JavaScript"
slice(产生新数组)
作用:截取某一范围到新数组
语法:
将所有从索引 start 到 end(不包括 end)的数组项复制到一个新的数组。
arr.splice(start[, deleteCount, elem1, …, elemN])
例子:
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s(复制从位置 1 到位置 3 的元素)
alert( arr.slice(-2) ); // s,t(复制从位置 -2 到尾端的元素)
注:不带参数地调用它:arr.slice() 会创建一个 arr 的副本。其通常用于获取副本,以进行不影响原始数组的进一步转换。
应用场景
复制和排序数组:我们有一个字符串数组 arr。我们希望有一个排序过的副本,但保持 arr 不变。 创建一个函数 copySorted(arr) 返回这样一个副本。
我们可以使用 slice() 来创建一个副本并对其进行排序:
function copySorted(arr) {
return arr.slice().sort();
}
let arr = ["HTML", "JavaScript", "CSS"];
let sorted = copySorted(arr);
alert( sorted ); // CSS, HTML, JavaScript
alert( arr ); // HTML, JavaScript, CSS (no changes)
concat(产生新数组)
作用:创建一个新数组,其中包含来自于其他数组和其他项的值
语法:
结果是一个包含来自于 arr,然后是 arg1,arg2 的元素的新数组。
arr.concat(arg1, arg2…)
它接受任意数量的参数 —— 数组或值都可以。
如果参数 argN 是一个数组,那么其中的所有元素都会被复制。否则,将复制参数本身。
例子:
let arr = [1, 2];
// 从 arr 和 [3,4] 创建一个新数组
alert( arr.concat([3, 4]) ); // 1,2,3,4
// 从 arr、[3,4] 和 [5,6] 创建一个新数组
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6
// 从 arr、[3,4]、5 和 6 创建一个新数组
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
通常,它只复制数组中的元素。其他对象,即使它们看起来像数组一样,但仍然会被作为一个整体添加:
let arr = [1, 2];
let arrayLike = {
0: "something",
length: 1
};
alert( arr.concat(arrayLike) ); // 1,2,[object Object]
但是,如果类数组对象具有 Symbol.isConcatSpreadable 属性,那么它就会被 concat 当作一个数组来处理:此对象中的元素将被添加:
let arr = [1, 2];
let arrayLike = {
0: "something",
1: "else",
[Symbol.isConcatSpreadable]: true,
length: 2
};
alert( arr.concat(arrayLike) ); // 1,2,something,else
二、遍历
forEach(不产生新数组,通过引用可以改变原数组)*
作用:遍历
语法:
arr.forEach(function(item, index, array) {
// … do something with item
});
例子:
["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
alert(`${item} is at index ${index} in ${array}`);
});
注意事项:
1、 forEach() 方法不创建新的数组而且返回值是undefined,数组中的每个元素调用回调函数的返回值也是undefined。
2、不支持break
3、使用return无效
4、forEach只能默认从索引0开始遍历
5、forEach删除自身元素index不会被重置
(for循环过程中支持修改索引(修改 i),但forEach做不到(底层控制index自增,我们无法左右它)
let arr=[1,2,3,4,5,6];
arr.forEach((item, index) => {
console.log(arr.splice(index, 1) );//依次打印[1] [3] [5]
index--;//无效
})
console.log(arr);//[2,4,6]
对forEach更深的理解:不可以直接修改元素,但是可以修改元素的属性
例1:
var a = [1,2,3,4,5]
a.forEach((item) => {
item = item * 2
})
console.log(a)
// [1,2,3,4,5]
原数组并没有发生改变。
例2:
var a = [1,'1',{num:1},true]
a.forEach((item, index, arr) => {
item = 2
})
console.log(a)
// [1,'1',{num:1},true]
这里修改item的值,依然没有修改原数组。
例3:
var a = [1,'1',{num:1},true]
a.forEach((item, index, arr) => {
item.num = 2
item = 2
})
console.log(a)
// [1,'1',{num:2},true]
当修改数组中对象的某个属性时,发现属性改变了。
综上:为什么会这样呢?
这里就要引入栈内存和堆内存的概念了,对于JS中的基本数据类型,如String,Number,Boolean,Undefined,Null是存在于栈内存中的,在栈内存中储存变量名及相应的值。而Object,Array,Function存在于堆内存中,在堆内存中储存变量名及引用位置。
在第一个例子中,为什么直接修改item无法修改原数组呢,因为item的值并不是相应的原数组中的值,而是重新建立的一个新变量,值和原数组相同。
在第二个例子中,数组中的对象的值也没有改变,是因为新创建的变量和原数组中的对象虽然指向同一个地址,但改变的是新变量的值,即新对象的值为2,原数组中的对象还是{num:1}。
在第三个例子中,由于对象是引用类型,新对象和旧对象指向的都是同一个地址,所以新对象把num变成了2,原数组中的对象也改变了。
正确改动原数组写法:
var a = [1,2,3,4,5]
a.forEach((item, index, arr) => {
arr[index] = item * 2
})
console.log(a)
// [2,4,6,8,10]
这个例子和例三其实同理,参数中的arr也只是原数组的一个拷贝,如果修改数组中的某一项则原数组也改变因为指向同一引用地址,而如果给参数arr赋其他值,则原数组不变。
=========================================================
补充:e.children是个对象,不能用forEach遍历!!!
三、在数组中搜索
indexOf和includes
作用:当数组元素是基本数据类型时,来查找搜索更方便
语法:
arr.indexOf(item, from) —— 从索引 from 开始搜索 item,如果找到则返回索引,否则返回 -1。
arr.includes(item, from) —— 从索引 from 开始搜索 item,如果找到则返回 true(译注:如果没找到,则返回 false)。
注:通常使用这些方法时只会传入一个参数:传入 item 开始搜索。默认情况下,搜索是从头开始的。
例子:
let arr = [1, 0, false];
alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1
alert( arr.includes(1) ); // true
请注意,indexOf 和 includes 使用严格相等 === 进行比较。所以,如果我们搜索 false,它会准确找到 false 而不是数字 0。
上述两种方法一般用于简单数组,即不是对象数组这种一层包一层的。如果我们想检查数组中是否包含元素 item,并且不需要知道其确切的索引,那么 arr.includes 是首选。
方法 includes 可以正确的处理 NaN 方法
includes 的一个次要但值得注意的特性是,它可以正确处理 NaN,这与indexOf 不同:const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1(错,应该为 0)
alert(arr.includes(NaN) );// true(正确)
这是因为 includes 是在比较晚的时候才被添加到 JavaScript中的,并且在内部使用了更新了的比较算法。
find和findIndex
作用:当数组元素是复杂数据类型时,如对象数组查找搜索特定条件的对象
语法:
let result = arr.find(function(item, index, array) {
// 如果返回 true,则返回 item 并停止迭代(即只返回第一个匹配的元素)
// 对于假值(falsy)的情况,则返回 undefined
});
例子:
我们有一个存储用户的数组,每个用户都有 id 和 name 字段。让我们找到 id == 1 的那个用户:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let user = users.find(item => item.id == 1);
alert(user.name); // John
arr.findIndex 方法(与 arr.find)具有相同的语法,但它返回找到的元素的索引,而不是元素本身。如果没找到,则返回 -1。
例子:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"},
{id: 4, name: "John"}
];
// 寻找第一个 John 的索引
alert(users.findIndex(user => user.name == 'John')); // 0
filter(产生新数组)
作用:范围的匹配,返回所有匹配元素组成的数组
语法:
let results = arr.filter(function(item, index, array) {
// 如果 true item 被 push 到 results,迭代继续
// 如果什么都没找到,则返回空数组
});
例子:
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
// 返回前两个用户的数组
let someUsers = users.filter(item => item.id < 3);
alert(someUsers.length); // 2
四、转换数组
map(产生新数组)
作用:它对数组的每个元素都调用函数,并将处理后的值返回给结果数组。
语法:
let result = arr.map(function(item, index, array) {
// 返回新值而不是当前元素
})
例子:
//将每个元素转换为它的字符串长度
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
alert(lengths); // 5,7,6
sort(修改原数组)
作用:arr.sort 方法对数组进行原位排序,更改元素的顺序。
语法:
let arr = [ 1, 2, 15 ];
// 该方法重新排列 arr 的内容
arr.sort();
alert( arr ); // 1, 15, 2
这些元素默认情况下被按字符串进行排序。
故需自己提供一个函数
let arr = [ 1, 2, 15 ];
arr.sort((a, b) => a - b;);//升序
alert(arr); // 1, 2, 15
reverse(修改原数组)
作用:反转arr中元素的顺序
例子:
let arr = [1, 2, 3, 4, 5];
arr.reverse();
alert( arr ); // 5,4,3,2,1
split(产生新数组)
作用:通过给定的分隔符将字符串分割成一个数组。
例子:
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
for (let name of arr) {
alert( `A message to ${name}.` ); // A message to Bilbo(和其他名字)
}
split 方法有一个可选的第二个数字参数 —— 对数组长度的限制。如果提供了,那么额外的元素会被忽略。但实际上它很少使用:
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);
alert(arr); // Bilbo, Gandalf
可将字符串拆分成字母数组:
let str = "test";
alert( str.split('') ); // t,e,s,t
join
作用:将数组粘成字符串
例子:
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串
alert( str ); // Bilbo;Gandalf;Nazgul
reduce
作用:累加
语法:
arr.reduce(callback(accumulator, currentValue,index, array),initialValue)
accumulato:上一次回调函数返回的累积值
currentValue:当前元素
index:当前元素索引值
array:调用reduce()的数组
reduce()方法initialValue参数:
initialValue: 第一次调用 callback函数时的第一个参数accumulator的值,如果没有提供初始值,则将使用数组中的第一个元素作为初始值
例子:
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
alert(result); // 0+1+2+3+4+5=15
// 删除 reduce 的初始值(没有 0)
let result = arr.reduce((sum, current) => sum + current);
alert( result ); // 1+2+3+4+5=15
如果数组为空,那么在没有初始值的情况下调用 reduce 会导致错误。
例如:
let arr = [];
// Error: Reduce of empty array with no initial value
// 如果初始值存在,则 reduce 将为空 arr 返回它(即这个初始值)。
arr.reduce((sum, current) => sum + current);
所以建议始终指定初始值。
应用场景!
1、有这样一组学生数据,按需求完成工作。
let students = [
{
no: 1,
name: "张三",
age: 18
},
{
no: 2,
name: "李四",
age: 19
},
{
no: 3,
name: "王五",
age: 16
},
{
no: 4,
name: "cyy",
age: 19
},
{
no: 5,
name: "xjy",
age: 17
}
]
(1)将学生的信息整理成’学号+下划线+名字’,输出给老师
let student_id=students.map(item=>({//箭头函数需写(),因为返回的是对象
id:`${item.no}_${item.name}`
}))
console.log(student_id);
(2)将学生的’学号+下划线+名字’,变成学生的id,存储进原来的students数据中
students.forEach(item=>{
item.id=`${item.no}_${item.name}`
})
console.log(students);
(3)筛选出这些学生中的已经成年的学生
let adults=students.filter(item=>item.age>=18)
console.log(adults);
(4)根据学号找出学生资料
function findStudent(no){
return students.find(item=>item.no==no);
}
let one=findStudent(1);
console.log(one);//{no: 1, name: '张三', age: 18}
(5)找出这些学生们年龄的分布并排序
//用includes
let ages=[];
students.forEach(item=>{
if(!ages.includes(item.age)){
ages.push(item.age);
}
})
ages.sort((a,b)=>{
return a-b;
})
console.log(ages);//[16, 17, 18, 19]
//用findIndex
let ages=[];
students.forEach((item,index)=>{
let sudentIndex=students.findIndex(stu=>item.age===stu.age)
if(sudentIndex==index){
ages.push(item.age)
}
})
console.log(ages);
(6)计算出这些学生们的平均年龄
let avgAge=students.reduce((sum,cur)=>{
return sum+cur.age;
},0)
console.log(avgAge/students.length);//17.8
(7)统计出这些学生们各个年龄的人数
let agesObj = students.reduce((prevCounts, item)=> {
if(item.age in prevCounts){
// item.age是key,prevCounts[item.age]是值
prevCounts[item.age] = prevCounts[item.age] + 1;
} else {
prevCounts[item.age] = 1
}
return prevCounts;
},{});
console.log(agesObj)//{16: 1, 17: 1, 18: 1, 19: 2}
=======================================================================
1、将诸如 “my-short-string” 之类的字符串由短划线分隔的单词变成骆驼式的 “myShortString”。
let str='my-dear-princess';
function camelize(str){
return str
.split('-')
.map((item,index)=>index==0?item:item[0].toUpperCase()+item.slice(1))
.join('');
}
let newstr=camelize(str);
console.log(newstr);//myDearPrincess
console.log(camelize("background-color") == 'backgroundColor'); //true
2、原位过滤范围:写一个函数 filterRangeInPlace(arr, a, b),该函数获取一个数组 arr,并删除其中介于 a 和 b 区间以外的所有值。检查:a ≤ arr[i] ≤ b。
该函数应该只修改数组。它不应该返回任何东西。
解:只能修改原数组,所以不能用filter;而涉及到数组的元素删减,也不能用forEach,故只能用for循环。
function ilterRangeInPlace(arr, a, b){
for(let i=0;i<arr.length;i++){
if(arr[i]<a||arr[i]>b){
arr.splice(i,1);
i--;
}
}
let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4); // 删除 1 到 4 范围之外的值
alert( arr ); // [3, 1]
3、我们有一个字符串数组 arr。我们希望有一个排序过的副本,但保持 arr 不变。
创建一个函数 copySorted(arr) 返回这样一个副本。
//我们可以使用 slice() 来创建一个副本并对其进行排序:
function copySorted(arr) {
return arr.slice().sort();
}
let arr = ["HTML", "JavaScript", "CSS"];
let sorted = copySorted(arr);
alert( sorted );// CSS, HTML, JavaScript
alert( arr );// HTML, JavaScript, CSS (no changes)
4、你有一个 user 对象数组,每个对象都有 user.name。编写将其转换为 names 数组的代码。
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let users = [ john, pete, mary ];
let names = users.map(item => item.name);
alert( names ); // John, Pete, Mary
5、你有一个 user 对象数组,每个对象都有 name,surname 和 id。编写代码以该数组为基础,创建另一个具有 id 和 fullName 的对象数组,其中 fullName 由 name 和 surname 生成。
let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };
let users = [ john, pete, mary ];
let usersMapped = users.map(user => ({
fullName: `${user.name} ${user.surname}`,
id: user.id
}));
/*
usersMapped = [
{ fullName: "John Smith", id: 1 },
{ fullName: "Pete Hunt", id: 2 },
{ fullName: "Mary Key", id: 3 }
]
*/
alert( usersMapped[0].id ); // 1
alert( usersMapped[0].fullName ); // John Smith
6、按年龄对用户排序:编写函数 sortByAge(users) 获得对象数组的 age 属性,并根据 age 对这些对象数组进行排序。
function sortByAge(arr) {
arr.sort((a, b) => a.age - b.age);
}
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let arr = [ pete, john, mary ];
sortByAge(arr);
// 排序后的数组为:[john, mary, pete]
alert(arr[0].name); // John
alert(arr[1].name); // Mary
alert(arr[2].name); // Pete
7、假设我们收到了一个用户数组,形式为:{id:…, name:…, age:… }。
创建一个函数 groupById(arr) 从该数组创建对象,以 id 为键(key),数组项为值。
处理服务端数据时,这个函数很有用。
let users = [
{id: 'john', name: "John Smith", age: 20},
{id: 'ann', name: "Ann Smith", age: 24},
{id: 'pete', name: "Pete Peterson", age: 31},
];
let usersById = groupById(users);
/*
// 调用函数后,我们应该得到:
usersById = {
john: {id: 'john', name: "John Smith", age: 20},
ann: {id: 'ann', name: "Ann Smith", age: 24},
pete: {id: 'pete', name: "Pete Peterson", age: 31},
}
*/
function groupById(array) {
return array.reduce((obj, value) => {
obj[value.id] = value;
return obj;
}, {})
}