【算法与数据结构】JS基础——数组

前言

Hello,大家好,这里是成功在家隔离四天的GIS宇宙,最近趁着不怎么忙碌,可以好好学习一下算法和数据结构了,本科期间基本学了和没学一样,所以接下来会出一个关于JavaScript算法和数据结构的专栏,本人水平有限,也是边学边记录,如果你喜欢这个专栏的话,可以点个赞,蟹蟹~

今天首先对JavaScript最基础的数组进行介绍,下一篇会介绍栈,感兴趣的赶紧关注我吧~

数组创建

  1. new 关键字
  2. 中括号
let daysOfWeek = new Array(); // {1} 
daysOfWeek = new Array(7); // {2} 
daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); // {3}

let daysOfWeek = []
let daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', ]

添加元素

1.尾部插入push

daysOfWeek.push(123)

2.头部插入unshift

头部操作,首先将 i 元素赋值给 i+1,然后在索引0的位置插入新值

daysOfWeek.unshift(-1)

如果自己写的话,则为数组右移,腾出头部位置,还多申明了一个数组位置

Array.prototype.insertFirstPosition = function(value) {
    
     
  for (let i = this.length; i >= 0; i--) {
    
     
    this[i] = this[i - 1]; 
  } 
  this[0] = value; 
}; 
numbers.insertFirstPosition(-1);

删除元素

1.尾部删除

daysOfWeek.pop()

2.头部删除

daysOfWeek.shift()

将所有元素都赋值给前一个元素,并删除最后一个值,length最后变为16。

如果直接左移(如下述),最后一个元素会报错变成undefined。

for (let i = 0; i < numbers.length; i++) {
    
     
  numbers[i] = numbers[i + 1]; 
}

任意位置添加删除元素

numbers.splice(5, 0, 2, 3, 4);

splice第一个参数表示想要删除或插入元素的索引值,即数组的位置,第二个是删除的个数,0表示不删除,2,3,4表示将2,3,4依次插入到数组中。

numbers.splice(5, 3, 2, 3, 4);

上述代码表示,从5索引开始删除3个元素,并插入后面的2,3,4数字。

多维数组

JavaScript 只支持一维数组,并不支持矩阵。但是,我们可以用数组套数组,实现矩阵或任一多维数组。代码也可以写成如下这样。

let averageTemp = [];
// day 1
averageTemp[0] = [];
averageTemp[0][0] = 72;
averageTemp[0][1] = 75;
averageTemp[0][2] = 79;
averageTemp[0][3] = 79;
averageTemp[0][4] = 81;
averageTemp[0][5] = 81;
// day 2
averageTemp[1] = [];
averageTemp[1][0] = 81;
averageTemp[1][1] = 79;
averageTemp[1][2] = 75;
averageTemp[1][3] = 75;
averageTemp[1][4] = 73;
averageTemp[1][5] = 73;

使用console.table()可以以表格的形式打印出来:

数组方法

除了上述提到的方法,Js数组还提供了很多方法,常用如下:

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]

2.迭代器

  1. every
  2. some
  3. forEach
  4. map
  5. filter
  6. reduce
function isEven(x) {
    
     
  // 如果 x 是 2 的倍数,就返回 true 
  console.log(x); 
  return x % 2 === 0 ? true : false; 
} 
// ES2015(ES6)简写: 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 : 当返回false的时候停止
numbers.every(isEven);
// 1

2. some : 当返回true的时候停止
numbers.some(isEven);
// 1
// 2

3. forEach : 相当于for,无返回值
numbers.forEach(x => console.log(x % 2 === 0));
// false true false true ...

4. map :相当于for,返回一个包含所有返回值的数组
const myMap = numbers.map(isEven);
//
[
  false, true,  false,
  true,  false, true,
  false, true,  false,
  true,  false, true,
  false, true,  false
]

5. filter :返回满足条件的值(即返回true的值),
const evenNumbers = numbers.filter(isEven);
//
[
   2,  4,  6, 8,
  10, 12, 14
]

6. reduce :接收一个有如下四个参数的函数:previousValue、currentValue、index和array。index和array是可选的参数。
函数式编程:返回数组的累加
const reduceNumbers = numbers.reduce((previous, current) => previous + current);
//  120

3.ES6新方法

for of 循环迭代

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
for (const n of numbers) {
    
     
  console.log(n % 2 === 0 ? 'even' : 'odd'); 
}

@@iterator对象

通过Symbol.iterator访问并执行函数,返回iterator对象,iterator对象有个next函数,每次执行,返回结果:{value: 2, done: false},通过.value访问值,done的值为false意味着还有可迭代的值。

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

也可以通过for of来循环输出

iterator = numbers[Symbol.iterator](); 
for (const n of iterator) {
    
     
  console.log(n); 
}

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
// 也可用for of 迭代
aEntries = numbers.entries(); 
for (const n of aEntries) {
    
     
  console.log(n); 
}

keys方法返回包含数组索引的@@iterator,value表示元素的索引,done属性的值为false,就意味着还有可迭代的值。

一旦没有可迭代的值,aKeys.next()就会返回一个value属性为undefined、done属性为true的对象。

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 }

Array.from 方法

Array.from可以根据已有数组创建一个新数组,也可根据函数过滤。

let numbers2 = Array.from(numbers);
[
   1,  2,  3,  4,  5,  6,
   7,  8,  9, 10, 11, 12,
  13, 14, 15
]
let evens = Array.from(numbers, x => (x % 2 == 0));
[
  false, true,  false,
  true,  false, true,
  false, true,  false,
  true,  false, true,
  false, true,  false
]

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(...numbers4);

fill 方法

fill方法用静态值填充数组。以下面的代码为例。

let numbersCopy = Array.of(1, 2, 3, 4, 5, 6);
// numbersCopy数组的length是 6,也就是有 6 个位置。再看下面的代码。
numbersCopy.fill(0);
// numbersCopy数组所有位置上的值都会变成 0([0, 0, 0, 0, 0, 0])。我们还可以指定开始填充的索引,如下所示。
numbersCopy.fill(2, 1); 
// 上面的例子里,数组中从 1 开始的所有位置上的值都是 2([0, 2, 2, 2, 2, 2])。 同样,我们也可以指定结束填充的索引。 
numbersCopy.fill(1, 3, 5); 
// 在上面的例子里,我们会把 1 填充到数组索引 3 到 5 的位置(不包括 3 和 5),得到的数组为[0, 2, 2, 1, 1, 2]。 
// 创建数组并初始化值的时候,fill方法非常好用,就像下面这样。 
let ones = Array(6).fill(1); 
// 上面的代码创建了一个长度为 6、所有值都是 1 的数组([1, 1, 1, 1, 1, 1])。

copyWithin 方法

copyWithin方法复制数组中的一系列元素到同一数组指定的起始位置。

array.copyWithin(target, start,end)

target为指定起始位置(必须),start为元素复制的起始索引位置,end为停止复制的索引位置 (默认为array .length)

let copyArray = [1, 2, 3, 4, 5, 6];
// 假如我们想把 4、5、6 三个值复制到数组前三个位置,得到[4, 5, 6, 4, 5, 6]这个数组,可以用下面的代码达到目的。
copyArray.copyWithin(0, 3);
// 假如我们想把 4、5 两个值(在位置 3 和 4 上)复制到位置 1 和 2,可以这样做:
copyArray = [1, 2, 3, 4, 5, 6];
copyArray.copyWithin(1, 3, 5);
// 这种情况下,会把从位置 3 开始到位置 5 结束(不包括 3 和 5)的元素复制到位置 1,结果是得到数组[1, 4, 5, 4, 5, 6]。

4.排序

反序输出数组numbers(它本来的排序是1, 2, 3, 4, …, 15)

numbers.reverse();

输出numbers的话就会看到[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],此时使用sort方法

numbers.sort();

结果会是[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]。看起来不大对,是吧?这是因为sort方法在对数组做排序时,把元素默认成字符串进行相互比较。

我们可以传入自己写的比较函数。因为数组里都是数,所以可以像下面这样写。
numbers.sort((a, b) => a - b);

b大于a时,这段代码会返回负数,反之则返回正数。如果相等的话,就会返回 0。也就是说返回的是负数,就说明a比b小,这样sort就能根据返回值的情况对数组进行排序。

之前的代码也可以表示成如下这样,会更清晰一些。

function compare(a, b) {
    
    
  if (a < b) {
    
    
    return -1;
  }
  if (a > b) {
    
    
    return 1;
  }
  // a 必须等于 b
  return 0;
}
numbers.sort(compare);

① 自定义排序

const friends = [ 
  {
    
     name: 'John', age: 30 }, 
  {
    
     name: 'Ana', age: 20 }, 
  {
    
     name: 'Chris', age: 25 }, // ES2017 允许存在尾逗号 
]; 
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));
//
[
  {
    
     name: 'Ana', age: 20 },
  {
    
     name: 'Chris', age: 25 },
  {
    
     name: 'John', age: 30 }
]

② 字符串排序

假如有这样一个数组。
let names = ['Ana', 'ana', 'john', 'John']; console.log(names.sort());
你猜会输出什么?答案如下所示。
["Ana", "John", "ana", "john"]
既然a在字母表里排第一位,为何ana却排在了John之后呢?这是因为 JavaScript 在做字
符比较的时候,是根据字符对应的 ASCII 值来比较的。例如,A、J、a、j对应的 ASCII 值分别是65、74、97、106。虽然a在字母表里是最靠前的,但J的 ASCII 值比a的小,所以排在了a前面。

更多关于 ASCII 表的信息,请访问 http://www.asciitable.com/。

如果使用sort来忽略大小写:

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']

你会发现数组没有变化,因为都变成小写来比较,所以当两者相同时,保持了原有的顺序。

如果希望小写字母排在前面,那么需要使用localeCompare方法。
names.sort((a, b) => a.localeCompare(b));

最后输出[ ‘ana’, ‘Ana’, ‘john’, ‘John’ ]

假如对带有重音符号的字符做排序的话,也可以用localeCompare来实现。
const names2 = ['Maève', 'Maeve'];

console.log(names2.sort((a, b) => a.localeCompare(b)));

最后输出的结果将是[“Maeve”, “Maève”]。

5.搜索

indexOf方法返回与参数匹配的第一个元素的索引;

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
console.log(numbers.indexOf(10)); 
// 9
console.log(numbers.indexOf(100));
// -1 没找到

lastIndexOf返回与参数匹配的最后一个元素的索引。

numbers.push(10); 
console.log(numbers.lastIndexOf(10)); 
// 15
console.log(numbers.lastIndexOf(100));
// -1

① ES6——find和findIndex方法

find和findIndex方法接收一个回调函数,搜索一个满足回调函数条件的值。下面的例子
里,我们要从数组里找一个 13 的倍数。find和findIndex的不同之处在于,find方法返回第一个满足条件的值,findIndex方法则返回这个值在数组里的索引。如果没有满足条件的值,find会返回undefined,而findIndex返回-1。

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

② ES7——includes方法

如果数组里存在某个元素,includes方法会返回true,否则返回false。使用includes
方法的例子如下。
console.log(numbers.includes(15)) // true

console.log(numbers.includes(20)) // false

可给includes方法传入一个起始索引,搜索会从索引指定的位置开始。

let numbers2 = [7,6,5,4,3,2,1];

console.log(numbers2.includes(4,5));// false,从第四位开始搜索,因此错过了5

6.输出字符串

① toString()方法

console.log(numbers.toString()); //1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

② join()方法

const numbersString = numbers.join('-');
console.log(numbersString); // '1-2-3-4-5-6-7-8-9-10-11-12-13-14-15'

7.官方文档

Array - JavaScript | MDN (mozilla.org)

类型数组

与C 和 Java 等其他语言不同,JavaScript 数组不是强类型的,因此它可以存储任意类型的
数据。类型数组则用于存储单一类型的数据。它的语法是let myArray = new TypedArray
(length),其中TypedArray需替换为下表所列之一。

let length = 5; 
let int16 = new Int16Array(length); 
for (let i=0; i<length; i++){
    
     
int16[i] = i+1; 
} 
console.log(int16); 

使用 WebGL API、进行位操作、处理文件和图像时,类型数组都可以大展拳脚。它用起来和普通数组毫无二致,本章所学的数组方法和功能都可以用于类型数组。https://www.html5rocks.com/en/tutorials/webgl/typed_arrays/是一个很好的教程,讲解了如何使用类型数组处理二进制数据,以及它在实际项目中的应用。

TypeScript中的数组

TypeScript 会在编译时进行类型检测,来确保只对所有值都属于相同数据类型的数组进行操作。

根据类型推断,TypeScript 能够理解numbers数组的声明和const numbers: number[]是一样的。

interface Person {
    
     
  name: string; 
  age: number; 
} 
 
// const friends: {name: string, age: number}[]; 与下述方式一样
const friends = [ 
  {
    
     name: 'John', age: 30 }, 
  {
    
     name: 'Ana', age: 20 }, 
  {
    
     name: 'Chris', age: 25 } 
]; 
 
function comparePerson(a: Person, b: Person) {
    
     
  // comparePerson 函数的内容 
}

总结

本文学习了JavaScript数组的基本用法,为下篇文章——栈做铺垫,也简单介绍了Typescript的数组,自己在学习的过程中也是学到了一些平时不怎么用的新东西,希望对你也有些帮助,这里是GIS宇宙,我们下期再见。

参考文献:

Array - JavaScript | MDN (mozilla.org)

学习JavaScript数据结构与算法第三版 Learning JavaScript Data Structures and Algorithms 3rd Edition ([巴西]格罗纳(LoianeGroner), 孙晓博, 邓钢, 吴双, 陈迪, 袁源) (z-lib.org)

本人其他平台账号:

  • 微信公众号:GIS宇宙
  • CSDN:GIS_宇宙
  • 知乎:GIS宇宙
  • 掘金:GIS宇宙

猜你喜欢

转载自blog.csdn.net/qq_41003479/article/details/128422794