数组
数组是最简单的内存数据结构,存储一系列同一种类型的值。虽然在JavaScript里,也可以在数组中保存不同类型的值,但我们还是遵守最佳实践,避免这么做(大多数语言都没这个能力)。
1.创建和初始化数组
// new关键字方式
let daysOfWeek = new Array();
daysOfWeek = new Array(7);
daysOfWeek = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
// 字面量方式
let daysOfWeek = [];
let daysOfWeek = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
console.log(daysOfWeek.length); // 7
访问元素和迭代数组
// 通过循环迭代数组、打印元素
for(let i = 0; i < daysOfWeek.length; i++) {
console.log(daysOfWeek[i]);
}
求斐波那契数列的前20个数。已知斐波那契数列中的前两项是1,从第三项开始,每一项都等于前两项之和。
const fibonacci = [];
fibonacci[1] = 1;
fibonacci[2] = 1;
for(let i = 3; i < 20; i++) {
fibonacci[i] = fibonacci[i-1] + fibonacci[i-2];
}
for(let i = 1; i < fibonacci.length; i++) {
console.log(fibonacci[i])
}
2.添加元素
假如我们有一个数组numbers,初始化成了0到9。
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
2.1 在数组末尾插入元素
numbers[numbers.length] = 10;
使用push方法
numbers.push(11);
numbers.push(12,13);
2.2 在数组开头插入元素
首先要腾出数组里第一个元素的位置,把所有元素向右移动一位。我们可以循环数组中的元素,从最后一位(长度值就是数组的末尾位置)开始,将对应的前一个元素(i-1)的值赋值给它(i),依次处理,最后把我们想要的值赋值给第一个位置(索引0)上。
Array.prototype.insertFirstPosition = function (value) {
for(let i = this.length; i >= 0; i--) {
this[i] = this[i-1];
}
this[0] = value;
}
numbers.insertFirstPosition(-1)
使用unshift方法
unshift方法背后的逻辑和insertFirstPosition方法的行为是一样的。
numbers.unshift(-2);
numbers.unshift(-4,-3);
3.删除元素
3.1 从数组末尾删除元素
numbers.pop();
3.1 从数组开头删除元素
Array.prototype.reIndex = function(myArray) {
const newArray = [];
for(let i = 0; i < myArray.length; i++) {
if(myArray[i] !== undefined) {
newArray.push(myArray[i]);
}
}
return newArray
}
Array.prototype.removeFirstPosition = function() {
for(let i = 0; i < this.length; i++) {
this[i] = this[i+1];
}
return this.reIndex(this);
}
numbers = numbers.removeFirstPosition();
使用shift方法
numbers.shift();
4.在任意位置添加或删除元素
// 删除从数组索引5开始的3个元素
numbers.splice(5,3);
// 把数2、3、4插入数组里,放到之前删除元素的位置上
numbers.splice(5,0,2,3,4);
// 从索引5开始删除了3个元素,但也从索引5开始添加了元素2、3、4
numbers.splice(5,3,2,3,4);
5.二维和多维数组
let averageTemp = [];
averageTemp[0] = [1,2,3,4];
averageTemp[1] = [5,6,7,8];
5.1 迭代二维数组的元素
function printMatrix(myMatrix) {
for(let i = 0; i < myMatrix.length; i++) {
for(let j = 0; j < myMatrix[i].length; j++) {
console.log(myMatrix[i][j])
}
}
}
5.2 多维数组
假设我们要创建一个3x3x3的矩阵,每一格里包含矩阵的i(行)、j(列)及z深度之和。
const matrix3x3x3 = [];
for(let i = 0; i < 3; i++) {
matrix3x3x3[i] = [];
for(let j = 0; j < 3; j++) {
matrix3x3x3[i][j] = [];
for(let z = 0; z < 3; z++) {
matrix3x3x3[i][j][z] = i+j+z;
}
}
}
用以下代码输出这个矩阵的内容
for(let i = 0; i < matrix3x3x3.length; i++) {
for(let j = 0; j < matrix3x3x3[i].length; j++) {
for(let z = 0; z < matrix3x3x3[i][j].length; z++) {
console.log(matrix3x3x3[i][j][z])
}
}
}
6.JavaScript的数组方法参考
6.1 数组合并
const zero = 0;
const positiveNumbers = [1,2,3];
const negativeNumbers = [-3,-2,-1];
let numbers = negativeNumbers.concat(zero,positiveNumbers); // [-3,-2,-1,0,1,2,3]
6.2 迭代器函数
假设数组中的值是从1到15;如果数组里的元素可以被2整除(偶数),函数就返回true,否则就返回false。
function isEven(x) {
// 如果x是2的倍数,就返回true
console.log(x);
return x % 2 === 0;
}
// 使用箭头函数改写isEven函数
const isEven = x => x % 2 === 0;
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
- 1.用every方法迭代 every方法会迭代数组中的每个元素,直到返回false。
numbers.every(isEven);
数组numbers的第一个元素是1,它不是2的倍数(1是奇数),因此isEven函数返回false,然后every执行结束。
- 2.用some方法迭代 some方法会迭代数组中的每个元素,直到返回true。
numbers.some(isEven);
numbers数组中的第一个偶数是2(第二个元素)。第一个被迭代的元素是1,isEven会返回false。第二个被迭代的元素是2,isEven返回true--迭代结束。
- 3.用forEach方法迭代 它和使用for循环的结果相同
numbers.forEach(x => {
console.log(x % 2 === 0)
})
- 4.使用map和filter方法
const myMap = numbers.map(isEven);
// [false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]
const eventNumbers = numbers.filter(isEven);
// [2,4,6,8,10,12,14]
- 5.使用reduce方法
numbers.reduce((previous,current) => previous + current);
// 120
6.3 ES6和数组的新功能
- 1.使用for...of迭代
for(const n of numbers) {
console.log(n % 2 === 0 ? 'even' : 'odd')
}
- 2.使用@@iterator对象 ES6为Array类增加了一个@@iterator属性,需要通过Symbol.iterator来访问。
let iterator = numbers[Symbol.iterator]();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
console.log(iterator.next().value); // 4
console.log(iterator.next().value); // 5
然后不断调用迭代器的next方法,就能依次得到数组中的值。
可以使用下面代码来输出numbers数组中的15个值。
iterator = numbers[Symbol.iterator]();
for(const n of iterator) {
console.log(n)
}
- 3.数组的entries、keys和values方法 entries方法返回包含键值对的@@iterator。
let aEntries = numbers.entries(); // 得到键值对的迭代器
console.log(aEntries.next().value); // [0,1] - 位置0的值为1
console.log(aEntries.next().value); // [1,2] - 位置1的值为2
console.log(aEntries.next().value); // [2,3] - 位置2的值为3
我们也可以使用下面的代码。
aEntries = numbers.entries();
for(const n of aEntries) {
console.log(n);
}
keys方法返回包含数组索引的@@iterator。
const aKeys = numbers.keys(); // 得到数组索引的迭代器
console.log(aKeys.next()); // {value: 0, done: false}
console.log(aKeys.next()); // {value: 1, done: false}
console.log(aKeys.next()); // {value: 2, done: false}
values方法返回的@@iterator则包含数组的值
const aValues = numbers.values();
console.log(aValues.next()); // {value: 1,done: false}
console.log(aValues.next()); // {value: 2,done: false}
console.log(aValues.next()); // {value: 3,done: false}
- 4.使用from方法 Array.from方法根据已有的数组创建一个新数组。
// 复制numbers数组
let numbers2 = Array.from(numbers);
// 传入过滤值的函数
let evens = Array.from(numbers,x => (x % 2 == 0))
- 5.Array.of方法 Array.of方法根据传入的参数创建一个新数组。
let numbers3 = Array.of(1);
let numbers4 = Array.of(1,2,3,4,5,6);
// 等同于
let numbers3 = [1];
let numbers4 = [1,2,3,4,5,6];
// 复制已有数组
let numbersCopy = Array.of(...numbers);
- 6.使用fill方法 fill方法用静态值填充数组。
let numbersCopy = Array.of(1,2,3,4,5,6);
numbersCopy.fill(0); // [0,0,0,0,0,0]
numbersCopy.fill(2,1); // [0,2,2,2,2,2]
numbersCopy.fill(1,3,5); // [0,2,2,1,1,2]
let ones = Array(6).fill(1); // [1,1,1,1,1,1]
- 7.使用copyWithin方法 copyWithin方法复制数组中的一系列元素到同一数组指定的起始位置。
let copyArray = [1,2,3,4,5,6];
copyArray.copyWithin(0,3); // [4,5,6,4,5,6]
copyArray = [1,2,3,4,5,6];
copyArray.copyWithin(1,3,5); // [1,4,5,4,5,6]
6.4 排序元素
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
// 反序输出数组numbers
numbers.reverse(); // [15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
numbers.sort(); // [1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]
sort方法在对数组做排序时,把元素默认成字符串进行相互比较。
我们可以传入自己写的比较函数。
numbers.sort((a,b) => a-b)
// 等价于
function compare(a,b) {
if(a < b) {
return -1;
}
if(a > b) {
return 1;
}
return 0;
}
numbers.sort(compare)
- 1.自定义排序 我们可以对任何对象类型的数组排序,也可以创建compareFunction来比较元素。例如,对象 Person有名字和年龄属性,我们希望根据年龄排序。
const friends = [
{ name: 'John', age: 30 },
{ name: 'Ana', age: 20 },
{ name: 'Chris', age: 25 }
]
function comparePerson(a,b) {
if(a.age < b.age) {
return -1;
}
if(a.age > b.age) {
return 1;
}
return 0;
}
console.log(friends.sort(comparePerson);
- 2.字符串排序
let names = ['Ana', 'ana', 'john', 'John'];
console.log(names.sort()); // ["Ana", "John", "ana", "john"]
JavaScript在做字符比较的时候,是根据字符对应的ASCII值来比较的。
let names = ['Ana', 'ana', 'john', 'John'];
console.log(names.sort((a,b) => {
if(a.toLowerCase() < b.toLowerCase()) {
return -1;
}
if(a.toLowerCase() > b.toLowerCase()) {
return 1;
}
return 0;
}))
// ["Ana", "ana", "john", "John"]
6.5 搜索
搜索有两个方法:indexOf方法返回与参数匹配的第一个元素的索引;lastIndexOf返回与参数匹配的最后一个元素的索引。
console.log(numbers.indexOf(10)); // 9
console.log(numbers.indexOf(100)); // -1
numbers.push(10);
console.log(numbers.lastIndexOf(10)); // 15
console.log(numbers.lastIndexOf(100)); // -1
- 1.ES6--find和findIndex方法
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
function multipleOf13(element,index,array) {
return (element % 13 == 0);
}
console.log(numbers.find(multipleOf13)); // 13
console.log(numbers.findIndex(multipleOf13)); // 12
find和findIndex的不同之处在于,find方法返回第一个满足条件的值,findIndex方法则返回这个值在数组里的索引。如果没有满足条件的值,find会返回undefined,而findIndex会返回-1.
- 2.ES7使用includes方法 如果数组里存在某个元素,includes方法会返回true,否则返回false
console.log(numbers.includes(15)); // true
console.log(numbers.includes(20)); // false
let numbers2 = [7,6,5,4,3,2,1];
console.log(numbers2.includes(4,5)); // false
6.6 输出数组为字符串
let numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
console.log(numbers.toString());
// 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
const numberString = numbers.join('-');
console.log(numberString);
// 1-2-3-4-5-6-7-8-9-10-11-12-13-14-15
7.类型数组
类型数组用于存储单一类型的数据。
let length = 5;
let int16 = new Int16Array(length);
let array16 = [];
array16.length = length;
for(let i = 0; i < length; i++) {
int16[i] = i + 1;
}
console.log(int16);
8.TypeScript中的数组
TypeScript会在编译时进行类型检测,来确保只对所有值都属于相同数据类型的数组进行操作。
9.小结
数组是最常用的数据结构,本节涉及了如何声明和初始化数组、给数组赋值以及添加和删除数组元素,还有二维、多维数组以及数组的主要方法。这对编写自己的算法很有用。
10.经典题目
题目来自《剑指offer》面试题3:二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的二维数组和一个整数,判断数组中是否含有该整数。
思路:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数字,剔除这个数字所在的行。也就是说,如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。
var find = function(nums,rows,columns,target) {
let found = false;
if(nums !== null && rows > 0 && columns > 0) {
let row = 0;
let column = columns -1;
while(row < rows && column >= 0) {
console.log(row,column)
if(nums[row][column] === target) {
found = true;
break;
}else if(nums[row][column] > target) {
--column;
}else {
++row;
}
}
}
console.log(found)
return found;
};
find([[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]],4,4,5)