iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
0

陣列是我們在寫程式時常用的資料類型之一,常用它來處理一組資料項目的集合。JavaScript 的陣列也是物件,擁有自己的方法。我們來看一下關於陣列的一些特性。熟練這些方法,你就不必依賴撰寫迴圈來處理陣列資料,工作也會比較簡單。

建立陣列

我們有二種方式可以建立陣列。

一個是使用 Array 建構器函式

var pokemons = new Array("Bulbasaur", "Charmander", "Squirtle");

另一種是使用 array literal。

var pokemons = ["Bulbasaur", "Charmander", "Squirtle"];

相信經驗老道的你一定都是用第二種吧?Array literal 不僅簡單易懂,輸入的字元比較少,而且 JavaScript constructor function 會有被覆寫的可能,這表示呼叫 new Array 不一定會建立出陣列。

Array length

陣列一定會有一個length屬性,用來表示陣列的大小。要取得陣列裡的項目,使用 index number,陣列的 index number 從 0 開始計算,最後一筆資料是第length - 1個項目。要存取超過length的項目,會得到undefined,因為項目並不存在,如同存取不存在的物件一樣。以上這些你應該都知道。

pokemons[0] // Bulbasaur
pokemons[2] // Squirtle
pokemons[4] // undefined

我們可以更換陣列項目的值,只要指定 index number。

console.log(pokemons);
// ["Bulbasaur", "Charmander", "Squirtle"]
pokemons[1] = "Charizard";
console.log(pokemons);
// ["Bulbasaur", "Charizard", "Squirtle"]

如果指定的 index number 超過 length 範圍,陣列會被延展,中間沒有給值的項目如同宣告了卻不給值的變數,會是undefined

pokemons[5] = "Vulpix";
console.log(pokemons); 
// ["Bulbasaur", "Charmander", "Squirtle", undefined, undefined, "Vulpix"]

因為length是一個屬性,我們也可以手動更改。把length增加的話,會新增undefined項目,減少的話,會從陣列後面刪除多餘的項目。

console.log(pokemons.length); // 6
pokemons.length = 8;
console.log(pokemons);
// ["Bulbasaur", "Charmander", "Squirtle", undefined, undefined, "Vulpix", undefined, undefined]
pokemons.length = 3;
console.log(pokemons);  
// ["Bulbasaur", "Charmander", "Squirtle"]

接著我們快速的看過內建的陣列方法。

新增或移除項目

  • push: 在陣列的最尾端增加一筆資料項目
  • unshift:在陣列的最前端增加一筆資料項目
  • pop:在陣列的最尾端移除一筆資料項目
  • shift:在陣列的最前端移除一筆資料項目
var pokemons = ["Bulbasaur", "Charmander", "Squirtle"];
pokemons.push("Pikachu");
// ["Bulbasaur", "Charmander", "Squirtle", "Pikachu"]
pokemons.unshift("Ditto");
// ["Ditto", "Bulbasaur", "Charmander", "Squirtle", "Pikachu"]
pokemons.pop();
// ["Ditto", "Bulbasaur", "Charmander", "Squirtle"]
pokemons.shift();
// ["Bulbasaur", "Charmander", "Squirtle"]

因為pushpop是動到尾端的項目,不會讓陣列重新調整,在處理大量資料時,會比shiftunshift來得快。

如果想要變更的項目不在陣列的頭尾,可以用splice

var pokemons = ["Bulbasaur", "Charmander", "Squirtle"];
var removed = pokemons.splice(1,1);
// pokemons: ["Bulbasaur", "Squirtle"]
// removed: ["Charmander"]

splice吃二個參數,第一個參數是刪除起始點的 index number,第二個參數是有幾筆資料要刪除。在這裡我要刪除pokemons[1]這一筆,所以用splice(1,1),用splice(1,2)的話”Charmander”, “Squirrel”二筆資料就會被刪掉。被刪除的項目陣列會成為splice的回傳值。

splice還支援在第三個參數開始輸入新的資料,會在指定的地方安插這些資料進入陣列。

陣列迭代

最常見的迭代方法是用for迴圈。

var pokemons = ["Bulbasaur", "Charmander", "Squirtle"];
for(var i = 0; i < pokemons.length; i++){
	console.log(pokemons[i]);
}

相信你對這樣的寫法已經習以為常了。不過要輸入這麼多奇奇怪怪的符號,還蠻考驗注意力,幸好 JavaScript 提供了forEach方法,我們寫一段 callback 函式,forEach會將陣列中的每一個項目逐一丟到 callback 函式中操作。上面的範例改寫的話會變成下面這樣。

pokemons.forEach(function(pokemon){
  // 陣列中的項目會逐一代入 pokemon 參數在此函式中執行
	console.log(pokemon);
});

陣列對應

我們現在有一組陣列,想用這組陣列來產生另一組陣列,可以用map方法。此方法也會將陣列中每一個項目逐一丟進 callback 函式裡,函式中的回傳值會 push 到一個新陣列,最終我們會得到一組陣列。

var pokemons = [
	{name: "Bulbasaur", snack: "Razz Berry"},
	{name: "Charmander", snack: "Nanab Berry"},
	{name: "Squirtle", snack: "Pinap Berry"},
	{name: "Pikachu", snack: "Sun Stone"}
];

// 我們的寶可夢訓練師 Ash 在超市想一次買齊所有寶可夢最愛零食,他需要一份購物清單
var snacks = pokemons.map(function(pokemon){
  // 陣列中的項目會逐一代入 pokemon 參數在此函式中執行
	// 回傳項目中的 snack 屬性值
	return pokemon.snack;
});
// snacks: ["Razz Berry", "Nanab Berry", "Pinap Berry", "Sun Stone"];

測試

在處理陣列時,經常會測試陣列裡的資料是否有滿足某種條件,JavaScript 提供了everysome二種方法。

every用在測試所有的資料項目是否符合給定的條件,只要有一個項目不符合,其結果會是false

const team = [
	{name: "Ash", age: 10},
	{name: "Misty", age: 11},
	{name: "Brock", age: 18},
	{name: "Tracey", age: 15},
];

const allOldEnough = team.every(function(person){
	return person.age >= 18;
});
// allOldEnough: false

Ash 的隊伍來到新的城鎮,他們打聽到市中心的酒吧可能有稀有寶可夢的情報,他們便想進去酒吧一探究竟。但是酒吧規定年滿18歲以上才能進入,門口的保鑣就用every檢查他們每一個人,可惜 Ash 第一個就不符合了,所以隊伍也就禁止進入。

const adultPresent = team.some(function(person){
	return person.age >= 18;
});
// adultPresent: true

如果酒吧的規定是只要有滿18歲的大人帶,就可以進入,這時保鑣改用some來檢查,雖然 Ash 和 Misty 年齡還沒到,但是 Brock 已經滿18了,就得到true的值,准許進入。

搜尋

ES6 中新增了陣列find方法,讓我們在尋找特定的陣列項目方便許多。否則以前要先建立迴圈,逐一檢查每一個項目,找到符合的項目就強迫中止迴圈,再回傳項目,實在是件麻煩的工作。

使用方法也是在find方法的參數使用一個 callback 函式,此函式會逐一檢驗陣列項目,當遇到第一個符合條件的項目,便會將項目回傳,並終止迴圈。如果全部檢查完了卻沒有任何一個項目符合,便會回傳false

除此之外還有filter的方法,用來找出陣列中所有符合條件的項目,最終集合在一個新陣列當中回傳。假如沒有任何一個項目符合篩選的條件,我們會得到一個空陣列。

const pokemons = [
	{name: "Bulbasaur", type: "grass"},
	{name: "Charmander", type: "fire"},
	{name: "Squirtle", type: "water"},
	{name: "Pikachu", type: "electric"},
	{name: "Pidgey", type: "flying"},
	{name: "Lapras", type: "water"},	
	{name: "Magikarp", type: "water"},
	{name: "Vulpix", type: "fire"}
];

const firstFirePokemon = pokemons.find(function(pokemon){
	return pokemon.type === "fire";
});
// {name: "Charmander", type: "fire"}

const allFirePokemons = pokemons.filter(function(pokemon){
	return pokemon.type === "fire";
});
// [{name: "Charmander", type: "fire"}, {name: "Vulpix", type: "fire"}]

Ash 將要挑戰一個帶著草系寶可夢的對手,於是他用find從自己的名單裡找出第一隻火系的寶可夢{name: "Charmander", type: "fire"},出來應戰。

Ash 成功的擊敗對手,但對方顯然不服氣,派出了更厲害的草系寶可夢 Venusaur!從來沒有遇過這麼強大的對手,Ash 不敢大意,於是用fiter叫出他所有的火系寶可夢[{name: "Charmander", type: "fire"}, {name: "Vulpix", type: "fire"}]出來,這將是場艱難的戰役。

排序

JavaScript 陣列內建了sort排序方法,關於它,你只要記住

Array.sort((a, b) => a - b);

就夠了。sort方法會在背後自動幫你處理。這個 callback 函式的意思是,JavaScript 會逐一比對陣列中的任二個項目,如果函式回傳小於 0, 那麼 a就應該在b之前,等於 0 表示二者相等,回傳值大於 0 的話,表示a要在b之後。

const numberArray = [4, 2, 5, 11, 1, 3];
numberArray.sort();
// numberArray: [1, 11, 2, 3, 4, 5]
numberArray.sort((a, b) => a - b);
// numberArray: [1, 2, 3, 4, 5, 11]

const numericStringArray = ["4", "2", "5", "11", "01", "3"];
numericStringArray.sort();
// numericStringArray: ["01", "11", "2", "3", "4", "5"]
numericStringArray.sort((a, b) => a - b);
// numericStringArray: ["01", "2", "3", "4", "5", "11"]

const stringArray = ["Pikachu", "Charmander", "Squirtle", "Bulbasaur"];
stringArray.sort();
// stringArray: ["Bulbasaur", "Charmander", "Pikachu", "Squirtle"]

由上面的範例能看到,sort方法雖然可以省略 callback 函式,在文字上面是沒問題的,會按照字母順序重排文字。由於sort是將參數轉成字串後才進行比對,因此數字字串會以第一個字元來比對,所以說 "11"會排在"2"的前面,這就不會是我們要的結果。這個時候就要代入 callback 函式(a,b) => a - b,讓我們的函式來進行比對的工作。

彙整

有時我們會需要處理一個陣列資料,最終得到一個值,例如加總陣列中所有的數字求總和。這時可以用reduce來做,它的用途是先將陣列中第一個項目和一個初始值(accumulator,預設是 0)丟進 callback 函式,經計算後的結果再和第二個陣列項目一起丟到函式裡計算,依此類推,最終得到一個值。

const array = [4, 2, 5, 1, 3];
const sum = array.reduce(accumulator, currentValue) => accumulator + currentValue);
// sum: 15

reduce的用處不止這樣,它可以接受更多的參數,在許多場合會很有用,請參考MDN的文件說明


上一篇
Day 20: proxy
下一篇
Day 22: Map
系列文
JavaScript 忍者的修練--從下忍進階到中忍30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言