為什麼我要放棄javaScript資料結構與演算法(第二章)——陣列
第二章 陣列
幾乎所有的程式語言都原生支援陣列型別,因為陣列是最簡單的記憶體資料結構。JavaScript裡也有陣列型別,雖然它的第一個版本並沒有支援陣列。本章將深入學習陣列資料結構和它的能力。
為什麼用陣列
需求:儲存所在城市每個月的平均溫度,可以這麼實現
var averageTemp1 = 43.3;
var averageTemp2 = 53.2;
var averageTemp3 = 14.2;
var averageTemp4 = 42.8;
var averageTemp5 = 14.8;
var averageTemp6 = 78.9;
只是儲存前六個月就用了6個變數,顯然這種方式不適合儲存這類需求。通過陣列可以簡單地實現我們的需求。
var averageTemp = [];
averageTemp[0] = 43.3;
averageTemp[1] = 53.2;
averageTemp[2] = 14.2;
averageTemp[3] = 42.8;
averageTemp[4] = 14.8;
averageTemp[5] = 78.9;
建立和初始化陣列
宣告、建立和初始化陣列的方式很簡單
var temp = new Array(); // 使用 new 關鍵字,簡單宣告並初始化一個陣列
var temp = new Array(8); // 還可以建立一個指定長度的陣列
var temp = new Array(1,2,4,9); // 直接將陣列元素作為引數傳遞給它的構造器
除了用 new
建立陣列,還可以通過中括號 []
簡單建立陣列。
var temp = [1,2,4,9];
訪問元素和迭代陣列
通過在陣列裡指定特定位置的元素,可以獲取該值或者賦值。而要知道一個陣列裡所有的元素,可以通過迴圈遍歷陣列。
for(var i = 0; i < temp.length; i++){
console.log(temp[i]); // 1,2,4,9
}
案例:斐波那契數列
已知斐波那契數列中的第一個數字是1,第二個數字是2,從第三項開始,每一項都等於前兩項之和。求斐波那契數列的前20個數
var fibonacci = [];
fibonacci[1] = 1;
fibonacci[2] = 2;
for(var i =3; i < 20; i++){
fibonacci[i] = fibonacci[i-1] + fibonacci[i-2];
}
console.log(fibonacci); // [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
新增元素
var number = [1,2,3];
number[number.length] = 4;
number // 1,2,3,4
上述程式碼可以在陣列的最後一位新增元素,但其實還有更加簡便的方法:
push
push 能新增任意個元素在陣列的末尾
number.push(5); // 5
number.push(6,7); //7
number // [1,2,3,4,5,6,7]
陣列使用 push 會返回陣列的長度
插入元素到陣列首位
首先我們要騰出陣列的第一個元素的位置,把所有的元素向右移動一位。我們可以迴圈陣列中的元素,從最後一位+1(長度)開始,將其對應的前一個元素的值賦給它,依次處理,最後把我們想要的值賦給第一個位置(-1)上。
for(var i = number.length; i>=0; i--){
number[i] = number[i-1];
}
number[0] = -0;
unshift
或者直接 使用 unshift 方法,可以將數值插入陣列的首位:
var number = [1,2,3,4];
number.unshift(-2); // 5
number.unshift(-4,-3); // 7
number // [-4, -3, -2, 1, 2, 3, 4]
陣列使用 unshift 會返回陣列的長度
刪除元素
從陣列尾部刪除元素
pop
要刪除最靠後的元素可以使用 pop 方法,會刪除並返回陣列的最後一個元素。如果陣列已經為空,則 pop() 不改變陣列,並返回 undefined 值。
var number = [1,2,3,4];
number.pop(); //4
number // [1,2,3]
number.pop() // 3
number // [1]
從陣列首部刪除元素
如果要移除陣列裡的第一個元素,可以使用下面的程式碼
var number = [1,2,3,4];
for(var i = 0;i < number.length; i++){
number[i] = number[i+1];
}
number // [2, 3, 4, undefined]
可以看出,我們將陣列左移了一位,但陣列的長度仍然沒有變化,這意味著陣列中有一個額外的元素,因為沒有定義,所以是 undefined
shift
shift() 方法用於把陣列的第一個元素從其中刪除,並返回第一個元素的值。 陣列的長度也會發生變化。如果陣列是空的,那麼 shift() 方法將不進行任何操作,返回 undefined 值。
小結
修改陣列的方法 | 描述 |
---|---|
push | push() 方法可向陣列的末尾新增一個或多個元素,並返回新的長度。 |
unshift | unshift() 方法可向陣列的開頭新增一個或更多元素,並返回新的長度。 |
pop | pop() 方法用於刪除並返回陣列的最後一個元素。 如果陣列已經為空,則 pop() 不改變陣列,並返回 undefined 值。 |
shift | shift() 方法用於把陣列的第一個元素從其中刪除,並返回第一個元素的值。 如果陣列是空的,那麼 shift() 方法將不進行任何操作,返回 undefined 值 |
push() 方法和 pop() 方法,能用陣列模擬基本的棧的資料結構(先進後出)。
shift()方法和unshift()方法,能用陣列模擬基本的佇列的資料結構(先進先出 )。
在任意位置新增或者刪除元素
已經知道如何刪除陣列開頭和結尾的元素,那麼該怎麼在陣列中的任意位置刪除或者新增元素?
splice
splice() 方法向/從陣列中新增/刪除專案,然後返回被刪除的專案。 splice() 方法可刪除從 index 處開始的零個或多個元素,並且用引數列表中宣告的一個或多個值來替換那些被刪除的元素。
語法
arrayObject.splice(index,howmany,item1,.....,itemX)
例子
var number = [1,2,3,4];
number.splice(2,0,4,4,5); // []
number //[1, 2, 4, 4, 5, 3, 4]
number.splice(2,5,7); // [4, 4, 5, 3, 4]
number //[1, 2, 7]
二維或者多維陣列
我們知道如果要記錄數天內每個小時的氣溫,可以使用陣列來儲存這些資料。那麼要儲存兩天每小時氣溫的資料的時候可以這樣。
var averageTemp1 = [32,53,45,23,46,53];
var averageTemp2 = [98,32,74,34,63,73];
然而這不是最好的方法。可以使用矩陣(二維陣列)來儲存這些資訊。矩陣的行儲存每天的資料,列對應小時級別的資料。
var averageTemp = [];
averageTemp[0] = [32,53,45,23,46,53];
averageTemp[1] = [98,32,74,34,63,73];
JavaScript只支援一維陣列,並不支援矩陣。但是,可以用陣列套陣列來模擬矩陣或者任一多維陣列。
迭代二維陣列的元素
如果想看到這矩陣的輸出,可以建立一個通用函式,專門輸出其中的值:
function printMatrix(x){
for(var i = 0; i < x.length; i++){
for(var j = 0; j< x[i].length; j++){
console.log(x[i][j]);
}
}
}
printMatrix(averageTemp);
多維陣列
我們也可以用這種方式來處理多維陣列。假如我們要建立一個3x3x3的矩陣,每一個格子裡包含矩陣的i(行)、j(列)、z(深度)之和:
var matrix3x3x3 = [];
for(var i = 0; i < 3; i++){
matrix3x3x3[i] = [];
for(var j = 0; j < 3; j++){
matrix3x3x3[i][j] = [];
for(var z = 0; z < 3; z++){
matrix3x3x3[i][j][z] = i+j+z;
}
}
}
資料結構中有幾個維度都沒有關係,都可以用迴圈遍歷每個維度來訪問所有格子
for(var i = 0; i < matrix3x3x3.length; i++){
for(var j = 0; j< matrix3x3x3[i].length; j++){
for(var z = 0; z < matrix3x3x3[i][j].length; z++){
console.log(matrix3x3x3[i][j][z]);
}
}
}
如果是一個3x3x3x3的矩陣,程式碼中就會用四層巢狀的 for 語句,以此類推。
JavaScript 的陣列方法參考
在JavaScript裡,陣列是可以修改的物件。這意味著建立的每一個陣列都有一些可用的方法。
下面表格是陣列的一些核心方法。
方法名 | 描述 |
---|---|
concat | 連線2個或者更多陣列,並返回結果 |
every | 對陣列中的每一項執行給定函式,如果該函式對每一項都但返回true,則返回true |
filter | 對陣列中度過每一項執行給定函式,返回該函式會返回true的項組成分陣列 |
forEach | 對陣列中更多每一項執行給定函式,這個方法沒有返回值 |
join | 將所有的陣列元素連線成一個字串 |
indexOf | 返回第一個與給定引數相等的陣列元素的索引,沒有找到則返回-1 |
lastIndexOf | 返回在陣列中搜尋到的與給定引數相等的元素的索引裡最大的值 |
map | 對陣列中的每一項執行給定函式,返回每次函式呼叫結果組成的陣列 |
reverse | 顛倒陣列中的元素的順序,原先第一個元素現在變成了最後一個,同樣原先的最後一個元素變成了現在的第一個 |
slice | 傳入索引值,將陣列裡對應索引範圍內的元素作為新陣列返回 |
some | 對陣列中每一項執行給定函式,如果任一項返回true,則返回true |
sort | 按照字母的順序對陣列排序,支援傳入指定排序方法的函式作為引數 |
toString | 將陣列作為字串返回 |
valueOf | 和 toString 相似,將陣列作為字串返回 |
陣列合並
有多個陣列,需要合併起來成為一個陣列。我們可以迭代各個陣列,然後把每個元素加入最終的陣列。
JavaScript也有提供相對應的方法 concat()
var a = 0;
var b = [1,2,3];
var c = [-3,-2,-1];
var s = c.concat(a,b);
s // [-3, -2, -1, 0, 1, 2, 3]
迭代器函式
有時候,我們需要迭代陣列中的元素。可以使用迴圈語句(前面提到的for語句等)。而其實 JavaScript 內建了許多陣列可以使用的迭代方法。
對於本節的例子,我們需要函式和陣列。假如有一個陣列,值是從1到15,如果陣列裡面的元素可以被2整除(偶數),函式就要返回true,否則就返回false:
var isEven = function(x){
// 如果是 2的倍數,就返回 true
return (x % 2 == 0);
}
var number = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
every
every 會迭代陣列中的每個元素,直到返回 false。
number.every(isEven)
在這個例子中,陣列number第一個元素是1,不是2的倍數,因此 isEven 函式返回false,然後 every 執行結束。
some
some 方法和 every 相似,不過some方法會迭代陣列中的每個元素,直到函式返回true
number.some(isEven)
這個例子中,陣列的第二個引數是2,為2的倍數,因此返回true,迭代結束
forEach
如果要迭代整個陣列可以用 forEach 方法,和使用 for 迴圈相同:
number.forEach(function(x){
console.log((x % 2 == 0));
});
map & filter
JavaScript還有兩個會返回新陣列的遍歷方法。第一個是 map:
var myMap = number.map(isEven);
myMap // [false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]
從上面程式碼可以看出,myMap儲存了傳入 map 方法的 isEven函式執行的結果。這樣就可以很容易知道一個元素是否偶數。
還有一個filter方法,它返回的新陣列由使函式返回 true 的元素組成:
var evenNumbers = number.filter(isEven);
evenNumbers // [2, 4, 6, 8, 10, 12, 14]
reduce
reduce() 方法接收一個函式作為累加器,陣列中的每個值(從左到右)開始縮減,最終計算為一個值。
語法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
引數 | 描述 |
---|---|
total | 必需。初始值, 或者計算結束後的返回值。 |
currentValue | 必需。當前元素 |
currentIndex | 可選。當前元素的索引 |
arr | 可選。當前元素所屬的陣列物件。 |
如果要對一個陣列中所有元素進行求和,這就很有用
number.reduce(function(total,currentValue,index){
return total + currentValue;
});
// 120
ES6 和陣列的新功能
下表是ES6/7新增的陣列方法
方法 | 描述 |
---|---|
@@iterator | 返回一個包含陣列鍵值對的迭代器物件,可以通過同步呼叫得到陣列元素的鍵值對 |
copyWithin | 複製陣列中一系列元素到同一陣列指定的起始位置 |
entries | 返回包含陣列所有鍵值對的@@iterator |
includes | 如果陣列中存在某個元素則返回 true,否則返回false,ES7新增 |
find | 根據回撥函式給定的條件從陣列中查詢元素,如果找到就返回該元素 |
findIndex | 根據回撥函式給定的條件從陣列中尋找元素,如果找到就返回該元素在陣列中的索引 |
fill | 用靜態值填充陣列 |
from | 根據已有陣列建立一個新陣列 |
keys | 返回包含陣列所有索引的@@iterator |
of | 根據傳入的引數建立一個新陣列 |
values | 返回包含陣列中所有值的@@iterator |
除了這些新的方法,還有一種用 for… of迴圈來迭代陣列的新做法,以及可以從陣列例項得到的迭代器物件。
使用 forEach 和箭頭函式
箭頭函式可以簡化使用 forEach迭代陣列元素的做法
number.forEach(function(x){
console.log (x % 2 == 0);
})
// 等於
number.forEach(x => {
console.log(x % 2 == 0);
});
使用 for…of 迴圈迭代
for(let n of number){
console.log(n % 2 == 0);
}
使用ES6新的迭代器(@@iterator)
ES6還為 Array 類增加了一個 @@iterator 屬性,需要通過 Symbol.iterator來訪問。
let iterator = number[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
console.log(iterator.next().value); // 6
因為number陣列中有15個值,所以需要呼叫15次 iterator.next().value ,陣列中所有值都迭代完之後,就會返回 undefined。
-
陣列的 entries、keys 和 values 方法
ES6還增加了三種從陣列中得到迭代器的方法。
entries 方法返回包含鍵值對的 @@iterator
let aEntries = number.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
number 陣列中都是數字,key是陣列中的位置,value是儲存在陣列中索引的值
使用集合、欄位、雜湊表等資料結構時,能夠取出鍵值對是很有用的。後面會詳細講解。
key 方法返回包含陣列索引的 @@iterator
let aKeys = number.entries(); // 得到陣列索引的迭代器 console.log(aKeys.next()); // { value: 0, done: false} console.log(aKeys.next()); // { value: 1, done: false} console.log(aKeys.next()); // { value: 2, done: false}
keys方法會返回number陣列的索引。一旦沒有可以迭代的值,aKeys.next() 就會返回一個value屬性為undefined,done屬性為 true的物件。如果 done屬性為false,就意味著還有可以迭代的值。
使用from方法
Array.from方法根據已有的陣列建立一個新數。比如複製number陣列:
let number2 = Array.from(number);
number2 // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
使用Array.of
Array根據傳入的引數建立一個新陣列、
let number3 = Array.of(1);
let number4 = Array.of(1,2,3,4,5,6);
number3 // [1]
number4 // [1,2,3,4,5,6]
// 複製已有的陣列
let numberCopy = Array.of(...number4);
numberCopy // [1,2,3,4,5,6]
使用fill方法
fill方法用靜態值充填陣列。
let numberCopy = Array.of(1,2,3,4,5,6);
numberCopy.fill(0); // [0, 0, 0, 0, 0, 0]
// 指定開始充填的索引
numberCopy.fill(2,1); // [0, 2, 2, 2, 2, 2]
// 指定結束的索引(結束的索引不包括本身)
numberCopy.fill(1,3,5); // [0, 2, 2, 1, 1, 2]
建立陣列並初始化的時候,fill 方法就非常好用。
let ones = Array(6).fill(1); // 建立了一個長度為6,所有值都是1的陣列
使用copyWithin方法
copyWithin方法複製陣列中的一系列元素到同一個陣列指定的起始位置。
語法:
array.copyWithin(target, start, end)
引數 | 描述 |
---|---|
target | 必需。複製到指定目標索引位置。 |
start | 可選。元素複製的起始位置。 |
end | 可選。停止複製的索引位置 (預設為 array.length)。如果為負值,表示倒數。 |
let copyArray = [1,2,3,4,5,6];
copyArray.copyWithin(0,3); // [4, 5, 6, 4, 5, 6]
let copyArray1 = [1,2,3,4,5,6];
copyArray1.copyWithin(1,3,5); // [1, 4, 5, 4, 5, 6]
排序元素
反序輸出最開始的長度為15的number陣列。
number.reverse();
// [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
嘗試使用JavaScript自帶的排序函式 sort();
number.sort();
//[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]
跟我們預期的有些不一樣,這是因為 sort 方法在對陣列進行排序 的時候,把元素預設成字串進行相互比較。所以我們要自己定義一個比較函式。
number.sort((a,b) =>{
return a -b;
})
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
上述程式碼,如果 b 大於 a,會返回負數,反之就是正數。如果相等的話,就會返回0。下面的寫法會清晰一點
function compare(a, b){
if(a < b){
return -1;
}
if(a > b){
return 1;
}
return 0;
}
number.sort(compare);
sort 方法接受 compareFunction作為引數來比較元素。然後sort 會用它來排序陣列
自定義排序
我們可以對任何物件型別的陣列排序,也可以建立 compareFuntion 來比較元素。例如物件 Person 有名字和屬性,我們希望根據年齡排序。
var friends = [
{name: `John`, age: 30},
{name: `Ana`, age: 20},
{name: `Chris`, age: 25}
];
friends.sort((a, b) =>{
return a.age - b.age;
})
// {name: "Ana", age: 20}
{name: "Chris", age: 25}
{name: "John", age: 30}
字串排序
var names = [`Ana`, `ana`, `John`, `john`];
names.sort();
// ["Ana", "John", "ana", "john"]
字串的比較是根據對應的 ASCⅡ值來比較的。例如 A、J、a、j對應分別是65、74、97、106。
雖然字母表的 a 是排靠前的,但是由於 ASCⅡ值 比較大,所以沒有排在首位。如果忽略大小寫呢?會是怎麼樣
names.sort((a, b) =>{
if(a.toLowerCase() < b.toLowerCase()){
return -1;
}
if(a.toLowerCase() > b.toLowerCase()){
return 1;
}
return 0;
})
// ["Ana", "ana", "John", "john"]
搜尋
搜尋有兩個方法:indexOf方法返回與引數匹配的第一個元素的索引,lastIndexOf返回與引數匹配的最後一個元素的索引。
number.indexOf(10); // 9
number.indexOf(100); // -1 (因為100不存在陣列裡面)
number.puhs(10);
number.lastIndexOf(10); // 15
number.lastIndexOf(100) // -1
ES6 find 和 findIndex方法
let number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
const multipleof13 = (element, index, array) => {
return (element % 13 == 0);
}
number.find(multipleof13); //13
number.findIndex(multipleof13); // 12
find 和 findIndex方法接受一個回撥函式,搜尋一個滿足回撥函式條件的值。上面的例子中,我們要從陣列裡找有個13的倍數。
ES7 使用includes方法
如果陣列存在某個元素,includes 方法就會返回 true,否則就返回 false。
number.includes(15) // true
number.includes(20) // false
number.includes(4,4) // false 第二個引數為開始搜尋的索引
輸出字串
toString 和 jion 方法
如果想把陣列裡所有元素輸出位一個字串,可以使用 toString 方法
number.toString(); // "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"
還可以用不同的分隔符將元素隔開
number.join(`-`); // "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15"
型別陣列
JavaScript由於不是強型別的,因此可以儲存任意型別的資料,而型別陣列則用於儲存單一的資料。
語法:
let myArray = new TypedArray(length);
型別陣列 | 資料型別 |
---|---|
Int8Array | 8位二進位制補碼整數 |
Unit8Array | 8位無符號整數 |
Unit8ClampedArray | 8位無符號整數 |
Int16Array | 16位二進位制補碼整數 |
Unit16Array | 16位無符號整數 |
Int32Array | 32位二進位制補碼整數 |
Unit32Array | 32位無符號整數 |
Float32Array | 32位IEEE浮點數 |
Float64Array | 64位IEEE浮點數 |
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); // [1, 2, 3, 4, 5]
使用 webGl API、進行位操作、處理檔案和影像時候,型別陣列都可以大展拳腳。它用起來和普通陣列也毫無二致,本節所學的陣列方法和功能都可以用於型別陣列。
小結
學習了常見的資料結構:陣列。學習了怎麼宣告和初始化陣列,給陣列賦值後,以及新增和移除陣列元素,學了多維陣列和陣列的一些操作方法。
下一章,學習棧,一種具有特殊行為的陣列。
書籍連結: 學習JavaScript資料結構與演算法
相關文章
- 資料結構與演算法——陣列資料結構演算法陣列
- 我們為什麼要學習資料結構和演算法?(一)資料結構演算法
- JavaScript資料結構01 - 陣列JavaScript資料結構陣列
- 資料結構與演算法 | 陣列(Array)資料結構演算法陣列
- 【資料結構與演算法】——稀疏陣列資料結構演算法陣列
- 《JavaScript資料結構與演算法》筆記——第2章 陣列JavaScript資料結構演算法筆記陣列
- 為什麼要學資料結構?資料結構
- JavaScript 的資料結構和演算法 - 陣列篇JavaScript資料結構演算法陣列
- javascript資料結構與演算法-佇列JavaScript資料結構演算法佇列
- 為什麼要學習資料結構和演算法?資料結構演算法
- 《資料結構與演算法之美》為什麼要學習資料結構和演算法 (讀後感)資料結構演算法
- 資料結構與演算法學習-陣列資料結構演算法陣列
- 資料結構與演算法之稀疏陣列資料結構演算法陣列
- 資料結構與演算法:稀疏陣列(一)資料結構演算法陣列
- TypeScript演算法與資料結構-陣列篇TypeScript演算法資料結構陣列
- 資料結構與演算法—稀疏陣列和佇列資料結構演算法陣列佇列
- JavaScript資料結構之陣列棧佇列JavaScript資料結構陣列佇列
- js資料結構與演算法 陣列、棧部分JS資料結構演算法陣列
- 為什麼我認為資料結構與演算法對前端開發很重要?資料結構演算法前端
- 資料結構與演算法整理總結---陣列,連結串列資料結構演算法陣列
- 【資料結構與演算法】字串匹配(字尾陣列)資料結構演算法字串匹配陣列
- python演算法與資料結構-什麼是資料結構Python演算法資料結構
- 為什麼放棄jQueryjQuery
- 我為什麼放棄MySQL?選擇了MongoDBMySqlMongoDB
- 02Javascript資料結構與演算法 之 佇列JavaScript資料結構演算法佇列
- 【JavaScript 演算法與資料結構】JavaScript演算法資料結構
- 資料結構-陣列資料結構陣列
- 資料結構 - 陣列資料結構陣列
- 資料結構與演算法系列(一)陣列實現資料結構演算法陣列
- 資料結構與演算法知識點總結(1)陣列與連結串列資料結構演算法陣列
- 為什麼演算法和資料結構重要?演算法資料結構
- javascript資料結構與演算法-棧JavaScript資料結構演算法
- JavaScript資料結構與演算法(串)JavaScript資料結構演算法
- JavaScript資料結構與演算法——集合JavaScript資料結構演算法
- 資料結構與演算法基礎之指標和陣列資料結構演算法指標陣列
- 資料結構與演算法-佇列資料結構演算法佇列
- 資料結構之「陣列」資料結構陣列
- 資料結構之陣列資料結構陣列