Học bằng cách đọc theo thứ tự

Mảng — Thêm, Xóa và Lấy Giá Trị

Học mảng: lưu nhiều giá trị trong một biến, lấy phần tử ra, thêm và xóa ở hai đầu, cắt và thay thế một phần.

Nếu bạn giữ từng món trong giỏ hàng ở một biến riêng — item1 / item2 / item3 — thì cứ thêm một sản phẩm là phải thêm một biến mới và sửa lại cả đoạn code đếm số món. Với một mảng (array) (cấu trúc dữ liệu lưu nhiều giá trị theo thứ tự trong một biến), bạn gom bao nhiêu món cũng được vào một biến duy nhất, còn việc thêm hay xóa thì để cho các phương thức có sẵn của mảng lo.

Lưu Giá Trị Trong Mảng — index và length

Bạn tạo một mảng bằng cách liệt kê các giá trị, ngăn cách bằng dấu phẩy, trong cặp []. Mỗi giá trị riêng lẻ gọi là một phần tử (element) (một giá trị nằm trong mảng). Chuỗi và số có thể để lẫn với nhau, nhưng trong thực tế người ta thường cho vào cùng một loại giá trị để phần xử lý về sau đồng nhất.

Muốn lấy một phần tử ra, bạn viết index của nó (vị trí của phần tử, đếm từ 0) trong []. Nếu viết cart[1] = "Băng keo" thì chỉ phần tử ở vị trí đó bị thay.

cart[0] là món thứ 1, cart[1] là món thứ 2. Vì món thứ 1 mang index 0 nên cách đánh số này lệch 1 so với cách người ta vẫn đếm.

length là số phần tử. Viết không có dấu ngoặc đơn, như cart.length. Vì index bắt đầu từ 0 nên index của phần tử cuối là length trừ đi 1.

at(-1) đếm ngược từ cuối, nhờ vậy bạn lấy được phần tử cuối mà không phải đi qua length. at(-2) là phần tử thứ 2 tính từ cuối.

Đọc một index ngoài phạm vi cũng không báo lỗi — bạn nhận về undefined, giá trị cho biết ở đó chưa có gì.

Đếm Bằng index và length
cart[0]cart[1]cart[2]"Vở""Bút""Băng keo"at(-3)at(-2)at(-1)
Với 3 phần tử, cart.length là 3 và index cuối là 2. Đếm từ cuối bằng at(-1) trả về cùng phần tử đó.
const cart = ["Vở", "Bút", "Băng keo"];

console.log(cart[0]);        // Vở
console.log(cart[2]);        // Băng keo
console.log(cart.length);    // 3

// Phần tử cuối nằm ở length trừ 1, hoặc dùng at(-1)
console.log(cart[cart.length - 1]);   // Băng keo
console.log(cart.at(-1));             // Băng keo

// Đọc index ngoài phạm vi cũng không báo lỗi
console.log(cart[5]);        // undefined

Thêm và Xóa Ở Cả Hai Đầu — push / pop / shift / unshift

Nội dung giỏ hàng thay đổi mỗi lần thêm hoặc bớt một món. Nếu lần nào bạn cũng viết lại mảng bằng tay thì phải tự quản lý số lượng và từng index. Mảng có sẵn các phương thức thêm và xóa phần tử ở cả hai đầu, nên việc tính toán vị trí có thể giao cho chúng.

push(value) thêm một phần tử vào cuối và trả về số phần tử sau khi thêm. pop() xóa phần tử cuối và trả về chính phần tử vừa bị xóa.

Muốn hiển thị tên món vừa bị xóa, bạn gán giá trị trả về của phương thức này vào một biến, như const removed = cart.pop();.

unshift(value)shift() làm việc ở đầu mảng. unshift thêm vào đầu, còn shift xóa phần tử đầu. Khi bạn chèn một món vào đầu, index của mọi phần tử phía sau đều bị đẩy lùi 1 vị trí.

4 Phương Thức Này Tác Động Vào Đâu
unshift(value)thêm vào đầupush(value)thêm vào cuốimảng cart[Bút, Băng keo]shift()xóa ở đầupop()xóa ở cuối
Hai phương thức thêm trả về số phần tử sau khi thêm; hai phương thức xóa trả về phần tử vừa bị xóa. Cả bốn đều làm thay đổi chính `cart`.
const cart = ["Bút"];

console.log(cart.push("Băng keo"));   // 2 (số phần tử sau khi thêm)
console.log(cart.join(", "));           // Bút, Băng keo

console.log(cart.pop());                // Băng keo (phần tử vừa bị xóa)
console.log(cart.join(", "));           // Bút

cart.unshift("Giấy nhớ");             // thêm vào đầu
console.log(cart.join(", "));           // Giấy nhớ, Bút

console.log(cart.shift());              // Giấy nhớ (phần tử vừa bị xóa)
console.log(cart.join(", "));           // Bút

Có thể bạn thấy lạ khi cart khai báo bằng const mà nội dung vẫn thay đổi được. const chỉ cấm gán một giá trị khác cho chính tên biến đó, như cart = someOtherArray, chứ không ngăn bạn thay đổi nội dung bên trong mảng, như cart.push(value). Lý do có sự khác biệt này sẽ được giải thích ở bài sau.

Mô phỏng lại việc thêm một món vào giỏ hàng rồi xóa món đó đi. cart đã được khai báo sẵn.

① Hãy thêm một chiếc bút máy vào cuối giỏ hàng.

② Hãy hiển thị tên món đang nằm ở cuối giỏ hàng.

③ Hãy hiển thị số món sau khi thêm.

④ Hãy xóa món cuối và hiển thị tên món vừa bị xóa.

⑤ Hãy hiển thị phần còn lại của giỏ hàng, nối bằng ", ".

(Nếu chạy đúng, phần giải thích sẽ xuất hiện.)

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Chèn một việc phát sinh vào đầu danh sách công việc hôm nay. tasks đã được khai báo sẵn.

① Hãy thêm "Liên hệ ban đầu về sự cố" vào đầu tasks.

② Hãy hiển thị các công việc theo đúng thứ tự, nối bằng " → ".

③ Hãy lấy công việc đầu tiên ra và hiển thị tên công việc đó.

④ Hãy hiển thị công việc đã lên đầu sau khi lấy ra.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Kiểm Tra Một Giá Trị Có Trong Mảng Không — includes và indexOf

Khi bạn thêm lại cùng một sản phẩm vào danh sách yêu thích mà chỉ gọi push, cùng một tên sẽ xuất hiện hai lần. Nếu kiểm tra xem giá trị đó đã có trong mảng chưa rồi mới thêm, bạn tránh được trùng lặp.

includes(value) trả về true nếu giá trị đó có trong mảng, false nếu không. Khi cần biết cả vị trí, bạn dùng indexOf(value) — nó trả về index của lần khớp đầu tiên. Không tìm thấy thì trả về -1.

Cách nghĩ giống hệt includes / indexOf của chuỗi, chỉ khác là ở đây bạn tìm một phần tử chứ không phải một ký tự.

Cả hai đều so sánh cả kiểu lẫn giá trị giống như ===, nên khác kiểu là coi như khác giá trị. Với một mảng toàn số, dù bạn tìm bằng chuỗi "2" thì cũng không có phần tử nào được coi là khớp.

includes và indexOf Trả Về Gì
includes("Giấy nhớ")Có trong mảngtrueindexOf("Bảng kẹp")Thứ 2trong mảng1indexOf("Tẩy")Không cótrong mảng-1
Đây là giá trị trả về khi danh sách yêu thích là ["Giấy nhớ", "Bảng kẹp"]. Chỉ cần biết có hay không thì dùng includes, cần cả vị trí thì dùng indexOf. indexOf trả về -1 khi không tìm thấy.
const favorites = ["Giấy nhớ", "Bảng kẹp"];

console.log(favorites.includes("Giấy nhớ"));   // true
console.log(favorites.includes("Tẩy"));  // false

console.log(favorites.indexOf("Bảng kẹp"));   // 1
console.log(favorites.indexOf("Tẩy"));   // -1

// Cách so sánh giống ===, khác kiểu là không khớp
const quantities = [1, 2, 3];
console.log(quantities.includes("2"));           // false

Đừng Đưa Kết Quả indexOf Thẳng Vào Điều Kiện

Khi giá trị được tìm thấy ở vị trí đầu tiên, indexOf trả về 0. Vì 0 là falsy nên nếu viết if (favorites.indexOf(name)), đúng phần tử đứng đầu lại bị coi là "không có". Chỉ cần biết có hay không thì dùng includes; còn khi dùng indexOf, hãy so sánh kết quả một cách rõ ràng, như !== -1.

Kiểm tra xem sản phẩm sắp thêm vào danh sách yêu thích đã có sẵn trong đó chưa. favorites / selected / newItem đã được khai báo sẵn.

① Hãy hiển thị selected có nằm trong favorites hay không.

② Hãy hiển thị newItem có nằm trong favorites hay không.

③ Hãy hiển thị selected nằm ở vị trí nào trong favorites.

④ Hãy hiển thị vị trí trả về khi tìm newItem.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Lấy Ra và Thay Thế Một Phần Của Mảng — slice và splice

Có lúc bạn chỉ muốn hiện top 3 của bảng xếp hạng bán chạy, có lúc lại cần thay đúng một mục trong danh sách phương thức vận chuyển. Điểm khác nhau giữa hai việc này nằm ở chỗ bạn lấy ra mà vẫn giữ nguyên mảng gốc, hay thay đổi luôn chính mảng gốc.

slice(start, end) trả về một mảng mới gồm các phần tử từ index bắt đầu cho đến ngay trước index kết thúc. Mảng gốc không đổi.

Nếu bỏ qua tham số kết thúc, nó lấy đến hết mảng. Cách đếm giống hệt slice của chuỗi.

splice(start, deleteCount, itemsToAdd) thay đổi trực tiếp chính mảng gốc. Nó xóa deleteCount phần tử tính từ start, và nếu bạn viết thêm các giá trị cần chèn thì chúng được đưa vào đúng vị trí đó.

Giá trị trả về là một mảng chứa các phần tử đã bị xóa, nên khi chỉ xóa một phần tử, bạn đọc nội dung bằng [0].

Cú phápTrả vềMảng gốc
slice(start, end)Mảng mới gồm các phần tử đã lấy raKhông đổi
splice(start, deleteCount)Mảng các phần tử đã bị xóaBị thay đổi
splice(start, deleteCount, itemsToAdd)Mảng các phần tử đã bị xóaBị thay đổi
Phương Thức Làm Thay Đổi Mảng Gốc và Phương Thức Không
pushthêm vào cuốiunshiftthêm vào đầusplicexóa và chènpopxóa ở cuốishiftxóa ở đầuslicelấy raincludestrả về có/khôngindexOftrả về vị tríLàm thay đổi mảng gốcMảng gốc không đổi
5 phương thức ở trên làm thay đổi chính mảng ngay khi được gọi. 3 phương thức ở dưới chỉ đọc mảng gốc, nên gọi bao nhiêu lần thì nội dung vẫn vậy.
const ranking = ["Bút", "Băng keo", "Giấy nhớ", "Bảng kẹp"];

// slice trả về mảng mới; ranking không đổi
const top2 = ranking.slice(0, 2);
console.log(top2.join(", "));       // Bút, Băng keo
console.log(ranking.length);        // 4

// splice thay đổi trực tiếp ranking
const removed = ranking.splice(1, 2, "Tẩy");
console.log(removed.join(", "));    // Băng keo, Giấy nhớ
console.log(ranking.join(", "));    // Bút, Tẩy, Bảng kẹp
console.log(ranking.length);        // 3

slice và splice Chỉ Khác 1 Chữ Nhưng Kết Quả Khác Nhau

Tên gọi na ná nhau, nhưng slice giữ nguyên mảng gốc và trả về một mảng mới, còn splice thì thay đổi luôn mảng gốc. Nếu chỉ để hiển thị một danh sách mà bạn lại gọi splice thì cứ mỗi lần hiển thị, dữ liệu gốc lại vơi đi một ít. Khi chỉ cần lấy ra, hãy chọn slice.

Cắt ra phần bạn muốn hiển thị từ bảng xếp hạng bán chạy. ranking gồm 5 mục và đã được khai báo sẵn.

① Hãy lấy top 3 ra và gán vào một biến riêng.

② Hãy hiển thị 3 mục vừa lấy ra, nối bằng ", ".

③ Hãy lấy các mục từ hạng 4 trở đi và hiển thị số lượng.

④ Hãy hiển thị số lượng của ranking ban đầu.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Thay một mục trong danh sách phương thức vận chuyển. shipping đã được khai báo sẵn.

① Hãy thay phương thức thứ 2 bằng "Giao buổi tối" và gán phần tử bị xóa vào một biến.

② Hãy hiển thị tên phương thức vừa bị xóa.

③ Hãy hiển thị danh sách sau khi thay, nối bằng ", ".

④ Hãy hiển thị số lượng sau khi thay.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Với const items = ["Bút", "Băng keo", "Giấy nhớ"];, length và index của phần tử cuối là bao nhiêu?

Câu 2cart.pop() trả về giá trị nào?

Câu 3Phương thức nào dưới đây không làm thay đổi mảng gốc khi được gọi?