[資料結構]Array — 陣列

Array 是資料結構的一種,概念就像置物櫃一樣,每個櫃子都可以存放資料並且都有自己的編號稱為索引值 index,Array 為連續的記憶體位置,因此讀取資料非常快速,只要透過 array[index]就可以拿到資料了,時間複雜度為 O(1),不過如果需要新增或刪除資料,就會有牽一髮動全身的問題,假如刪除了陣列的第一個資料,則後續的資料都需要向前遞補,時間複雜度為 O(n),n 為陣列的長度。

以下就來介紹陣列常用的方法

宣告一個空陣列

1
let arr = [];

在建立陣列時就先賦值

1
let memberList = ["Tom", "Jack", "Alice"];

Array[index]

1
取得特定位置的值;

memberList[0]; //‘Tom’

Array.length

獲取陣列的長度

1
memberList.length; //3

Array.pop()

移除陣列最後一個值,並回傳移除的項目

1
memberList.pop(); // 'Alice'

Array.push(value)

在陣列的最後面新增一個值

1
memberList.push("Rose");

Array.shift()

移除陣列的第一個值,並回傳移除的項目

1
memberList.shift();

Array.unshift(value)

在陣列的最前面新增一個值

1
memberList.unshift("Shane");

Array.indexOf(value)

尋找目標對象在陣列中的索引值,如果找不到的話就會回傳-1

1
memberList.indexOf("Jack"); //1

Array.lastIndexOf(value)

取指定對象最後一個出現的索引值,如果找不到的話就會回傳-1

1
2
3
let student = ["Tom", "Marry", "Waston", "Tom"];
let lastPosition = student.lastIndexOf("Tom");
console.log(lastPosition); //3

Array.includes(value)

檢查該陣列是否有這個值,有的話回傳 true,沒有的話回傳 false

1
memberList.includes("Jack"); //true

Array.findIndex(value)

尋找目標對象在陣列中的索引值,如果找不到的話就會回傳-1

1
2
3
4
5
6
7
8
9
10
let student = [
{ id: 1, name: "Tom" },
{ id: 2, name: "Marry" },
{ id: 3, name: " Waston" },
{ id: 4, name: "Tina" },
];
let res = student.findIndex((item) => {
return item.name == "Marry";
});
console.log(res); //1

Array.slice(begin, end)

用來切割陣列,不會改變原陣列

  • begin : 起始的 index ,若為-1 則代表從倒數第一個開始算
  • end: 結束的 index ,若不填則代表選取到陣列最後一個元素
1
2
3
let memberList = ["Tom", "Jack", "Alice", "Zora", "Mike"];
memberList.slice(0, 2); //\["Tom", "Jack"\]
console.log(memberList); //\['Tom', 'Jack', 'Alice', 'Zora', 'Mike'\];

如果想要用 slice 做淺拷貝可以這樣寫

1
memberList.slice();

Array.splice(begin, deleteCount, addItem)

用來切割陣列,會回傳切除的部位,當陣列經過 splice 的處理之後,原陣列也會跟著改變。

  • begin : 起始 index 若為-1 則代表從倒數第一個開始算
  • deleteCount : 要刪除的個數
  • addItem: 要添加的元素
1
2
3
let memberList = ["Tom", "Jack", "Alice", "Zora", "Mike"];
memberList.splice(0, 2); //\["Tom", "Jack"\]
console.log(memberList); //\["Alice", "Zora", "Mike"\] 原陣列被修改了

//帶入第三個參數 addItem

1
2
3
let memberList = ["Tom", "Jack", "Alice", "Zora", "Mike"];
memberList.splice(0, 2, "Dan"); //["Tom", "Jack"]
console.log(memberList); //["Dan", "Alice", "Zora", "Mike"]

Array.forEach(callback)

遍歷陣列除了傳統的 for 迴圈之外,也可以使用 forEach 或是 for of

1
2
3
4
5
6
7
8
9
//forEach
memberList.forEach((item, index, array) => {
console.log(`name${item}`, `index${index}`);
});

//for of
for (let value of memberList) {
console.log("name", value);
}

Array.map(callback)

類似工廠的概念,陣列的元素會被逐一加工,最後會回傳一個處理過後陣列

1
2
3
4
5
let student = ["Tom", "Marry", "Waston", "Petter"];
let result = student.map((item) => {
return `**${item}**`;
});
console.log(result); //['**Tom**', '**Marry**', '**Waston**', '**Petter**']

Array.filter(callback)

過濾符合條件想要篩選出來的對象,若沒有則回傳空陣列

1
2
3
4
5
6
let family = ["Tom", "Marry", "Waston", "Petter"];
let res = family.filter((item) => {
return item.includes("o"); //搜尋誰的名字裡含有"o"的
});

console.log(res); //['Tom', 'Waston']

Array.find(callback)

面試很喜歡問 find 跟 filter 的差異,filter 會回傳一個陣列包含所有符合條件的對象,find 則是只能回傳第一個符合條件的對象,沒有的話則 return undefined

1
2
3
4
5
6
let family = ["Tom", "Marry", "Waston", "Petter"];
let res = family.find((item) => {
return item.includes("o"); //搜尋誰的名字裡含有"o"的
});

console.log(res); //'Tom' 只回傳第一個找到的

Array.every(callback)

檢查陣列的每一個項目是否符合條件,只要有一個對象不符合就 return false

1
2
3
4
5
let list = ["tea", "soda", "water"];
let res = list.every((item) => {
return item.length == 3; //是否每個項目的長度都大於 3
});
console.log(res); //false

Array.some(callback)

陣列中只要有一個對象符合條件,就 return true

1
2
3
4
5
let list = ["tea", "soda", "water"];
let res = list.some((item) => {
return item.length >= 5; //是否項目的長度大於 5
});
console.log(res); //true

Array.sort(callback)

將陣列由小到大排序,字母的話按照 a-z,如果沒有帶入 callback function,就會將陣列每個項目轉換成字串來比對,不過這裡就會發現一個問題,為甚麼 300 會排在 55 的前面?因為以比對字串來說,300 的「3」小於 55 的「5」,因此才會出現 300 比 55 還小的這種詭異情形。

1
2
3
4
5
6
7
8
9
let family = ["Tom", "Marry", "Waston", "Petter"];
family.sort(); //['Marry', 'Tom', 'Tom', 'Waston']
let num = [12, 25, 55, 300];
num.sort(); //[12, 25, 300, 55]

num.sort((prev, next) => next - prev);
console.log(num); //[300, 55, 25, 12] 由大到小排列
num.sort((prev, next) => prev - next);
console.log(num); //[12, 25, 55, 300] 由小到大排列

Array.reverse()

反轉陣列的順序

1
2
3
let member = ["Tom", "Marry", "Waston", "Petter"];
let res = member.reverse();
console.log(res); //['Petter', 'Waston', 'Marry', 'Tom']

Array.concat()

將多個陣列串聯成同一個陣列

1
2
3
4
5
6

let teamA = ['Tom','Marry','Waston','Petter']
let teamB = ['Kevin','Joe','Vicky','Roy']let teamAll = teamA.concat(teamB)
console.log(teamAll)
//["Tom", "Marry", "Waston", "Petter", "Kevin", "Joe", "Vicky", "Roy"]

Array.reduce(callback)

將陣列的元素傳入 callback 處理,最後陣列會化作單一值,這邊引用了 MDN 的說明。

1
2
3
4
array.reduce(
callback(accumulator, currentValue, currentIndex, arr),
initialValue
);
  • accumulator

用來累積回呼函式回傳值的累加器(accumulator)或 initialValue。累加器是上一次呼叫後,所回傳的累加數值

  • currentValue

原陣列目前所迭代處理中的元素。

  • currentIndex (非必填)

原陣列目前所迭代處理中的元素之索引。若有傳入 initialValue,則由索引 0 之元素開始,若無則自索引值 1 之元素開始。

  • array(非必填)

呼叫 reduce() 方法的陣列。

  • initialValue(非必填)

於第一次呼叫 callback 時要傳入的累加器初始值。若沒有提供初始值,則原陣列的第一個元素將會被當作初始的累加器

1
2
3
4
5
6
let array = [1, 2, 3, 4];
let reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4
console.log(array.reduce(reducer));
// expected output: 10 // 5 + 1 + 2 + 3 + 4
console.log(array.reduce(reducer, 5));
// expected output: 15

因此用 reduce 來找陣列裡面最大或是最小的數字也是很方便的

1
2
3
4
5
6
let array = [1, 2, 3, 4];
let res = array.reduce((accumulator, currentValue) => {
return Math.max(accumulator, currentValue);
});
console.log(res);
//output: 5

Array.reduceRight(callback)

跟 reduce 差不多,不過順序變成由右至左,如果是做數字加總那結果是一樣的,如果是組字串那就可以看得出差異

1
2
3
4
5
6
7
8
9
10
let array = ["a", "b", "c", "d", "e"];
let left = array.reduce((prev, cur) => {
return prev + cur;
});
let right = array.reduceRight((prev, cur) => {
return prev + cur;
});

console.log(left); // "abcde"
console.log(right); // "edcba"

Array.from(target)

可以將類陣列轉換成陣列

1
Array.from(arrayLike, mapFn, this);
  • 類陣列(array-like)物件: 物件具有 length 屬性以及索引化(indexed)的元素
  • mapFn(非必填) :可以傳入 map function 來做進一步的處理
  • this(非必填): mapFn 函式執行時的 this 對象
1
2
Array.from("Hello");
//output: \['H','e','l','l','o'\]

假如今天我想要建立一個數字 1–99 的陣列可以怎麼做?一個一個慢慢寫當然是可以,只是寫完可能就天黑了,使用 Array from 搭配 map 一行輕鬆搞定。

1
Array.from({ length: 99 }, (v, i) => i + 1);

Array.copyWithin(target, start, end)

  • target ( 必填 ):先指定好要塞的位置
  • start: ( 非必填,預設 0 )然後決定要塞進的對象(從哪裡開始複製
  • end:( 非必填,預設等於陣列長度 )複製到哪個元素的前一個位置為止
1
2
3
4
5
let student = ["Tom", "Marry", "Waston", "Tom"];
let res = student.copyWithin(0, 2, 3);
console.log(res); //['Waston', 'Marry', 'Waston', 'Petter']
let result = student.copyWithin(-2, 1);
console.log(result); //['Waston', 'Marry', 'Marry', 'Waston']

Array.flat()

函數以遞迴方式將特定深度的子陣列重新串接成為一新的陣列,會傳入參數來指定巢狀陣列展開的深度,沒有傳的話預設是 1。

1
2
3
4
5
6
let arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
let arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(); // [1, 2, 3, 4, [5, 6]]
let arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2); // [1, 2, 3, 4, 5, 6]

Array.join()

將陣列裡的項目組成字串,可指定間隔字串

1
2
3
let family = ["Tom", "Marry", "Waston", "Petter"];
let str = family.join("!");
console.log(str); //"Tom!Marry!Waston!Petter"

Array.fill()

填滿的概念,把大家通通變成指定的元素,會改變原本的陣列

1
2
3
4
let student = ["Tom", "Marry", "Waston", "Tom"];
var res = student.fill("shane");
console.log(res);
//['shane', 'shane', 'shane', 'shane']

最後放上 Tommy 大大繪製的陣列操作大全,最常用陣列操作方法都列在上面了!