Map trong JavaScript là cấu trúc dữ liệu lưu các cặp khóa và giá trị. Khóa có thể thuộc bất kỳ kiểu nào. Nhiều người quen dùng object làm bảng tra cứu và vẫn ổn. Tuy nhiên Map có vài ưu điểm rõ ràng, nên bài này chỉ ra khi nào nên chuyển sang dùng nó.
Tạo Map trong JavaScript và các thao tác cơ bản

const diem = new Map();
diem.set("An", 8);
diem.set("Binh", 7);
diem.set("An", 9); // ghi đè giá trị cũ
console.log(diem.get("An")); // 9
console.log(diem.has("Chi")); // false
console.log(diem.size); // 2
diem.delete("Binh");
diem.clear();
Bốn phương thức cần nhớ là set, get, has và delete. Số lượng cặp lấy bằng thuộc tính size, không phải length như mảng. Nhầm hai tên này là lỗi rất hay gặp khi mới chuyển từ mảng sang Map.
Bạn cũng có thể khởi tạo Map từ một mảng các cặp: new Map([["a", 1], ["b", 2]]). Phương thức set trả về chính Map nên có thể nối chuỗi lời gọi cho gọn.
Duyệt qua Map

Map nhớ thứ tự thêm vào, và khi duyệt bạn nhận các phần tử theo thứ tự đó.
const gia = new Map([["ca phe", 30], ["tra", 25]]);
for (const [ten, g] of gia) {
console.log(ten, g);
}
gia.forEach((g, ten) => console.log(ten, g));
console.log([...gia.keys()]); // ["ca phe", "tra"]
console.log([...gia.values()]); // [30, 25]
Lưu ý thứ tự tham số của forEach: giá trị đứng trước, khóa đứng sau. Điều này ngược với thói quen nhiều người và là nguồn gây nhầm lẫn thường xuyên. Để chuyển Map thành mảng, dùng phép trải [...map] hoặc Array.from(map). Sau đó bạn dùng được mọi phương thức của mảng trong JavaScript.
Map khác object ở đâu

| Tiêu chí | Map | Object |
|---|---|---|
| Kiểu của khóa | Bất kỳ: chuỗi, số, đối tượng, hàm | Chuỗi hoặc symbol |
| Thứ tự duyệt | Theo thứ tự thêm vào | Số nguyên đứng trước, rồi đến chuỗi theo thứ tự thêm |
| Số lượng phần tử | Thuộc tính size | Phải đếm bằng Object.keys |
| Thêm xóa liên tục | Được tối ưu cho việc này | Không được thiết kế riêng cho việc này |
| Chuyển sang JSON | Không trực tiếp | Dùng JSON.stringify |
Object có sẵn các khóa kế thừa từ prototype như toString. Vì vậy kiểm tra obj["toString"] có thể cho kết quả bất ngờ. Map không mắc vấn đề đó. Ngược lại, object tiện hơn khi dữ liệu có cấu trúc cố định và cần chuyển qua JSON.
Khóa là đối tượng và vài điều cần biết

Điểm mạnh nhất của Map là khóa có thể là đối tượng. Ví dụ, bạn gắn thêm thông tin cho từng phần tử trên trang. Bạn không cần sửa chính phần tử đó.
const meta = new Map();
const nut = document.querySelector("button");
meta.set(nut, { luotBam: 0 });
nut.addEventListener("click", () => {
meta.get(nut).luotBam++;
});
Khóa được so sánh bằng thuật toán gần giống ===, nhưng coi NaN bằng NaN.
Hai đối tượng có nội dung giống nhau vẫn là hai khóa khác nhau. Chúng là hai tham chiếu riêng. Do đó map.get({a: 1}) sau khi set({a: 1}, ...) sẽ trả về undefined.
Nếu bạn muốn khóa là đối tượng nhưng không giữ chúng khỏi bị dọn bộ nhớ, hãy xem WeakMap. Cách trình duyệt thu hồi bộ nhớ được giải thích trong bài garbage collection trong JavaScript.
Chuyển đổi giữa Map và object
const m = new Map([["a", 1], ["b", 2]]);
const obj = Object.fromEntries(m); // { a: 1, b: 2 }
const lai = new Map(Object.entries(obj));
console.log(JSON.stringify(obj));
Hai hàm Object.fromEntries và Object.entries là cầu nối rất tiện. Chúng tôi thường dùng Map bên trong chương trình. Khi cần gửi dữ liệu qua mạng hoặc lưu vào tệp, ta chuyển sang object.
Ví dụ thực tế: gom nhóm dữ liệu
Một bài toán rất hay gặp là gom danh sách đơn hàng theo khách hàng. Map phù hợp vì khóa là mã khách, còn giá trị là mảng đơn hàng của khách đó.
const don = [
{ khach: "A", tien: 10 },
{ khach: "B", tien: 5 },
{ khach: "A", tien: 7 },
];
const nhom = new Map();
for (const d of don) {
if (!nhom.has(d.khach)) nhom.set(d.khach, []);
nhom.get(d.khach).push(d.tien);
}
console.log(nhom); // Map { "A" => [10, 7], "B" => [5] }
Mẫu code “kiểm tra có chưa, chưa thì tạo rồi thêm vào” này xuất hiện ở rất nhiều chương trình. Gom xong, bạn duyệt Map và dùng reduce trên mỗi mảng để tính tổng cho từng khách.
Set: người anh em của Map
Cùng họ với Map còn có Set, tập hợp các giá trị không trùng nhau. Muốn loại phần tử trùng khỏi một mảng, bạn chỉ cần [...new Set(mang)]. Cũng như Map, Set giữ thứ tự thêm vào và so sánh giá trị theo cách giống nhau. Khi chỉ cần biết một giá trị có thuộc tập hay không, Set gọn hơn Map.
Lỗi hay gặp với Map
- Dùng
map["khoa"] = 1thay chomap.set. Dòng này vẫn chạy. Nhưng nó chỉ thêm thuộc tính thường vào đối tượng Map, không lưu cặp khóa giá trị. - Gọi
JSON.stringify(map)và nhận về{}; hãy chuyển sang object hoặc mảng cặp trước. - Mong hai đối tượng giống hệt nhau thành cùng một khóa. Thực tế mỗi tham chiếu là một khóa riêng.
Khi dữ liệu có thể thiếu, hãy kết hợp với optional chaining để đọc giá trị gọn hơn.
Hỏi đáp nhanh
Map có nhanh hơn object không? Với dữ liệu thêm xóa thường xuyên, Map thường ổn định hơn. Với dữ liệu nhỏ và cố định, chênh lệch gần như không đáng kể. Hãy chọn theo mức độ rõ ràng của code.
Bạn có thể đọc thêm về mảng trong JavaScript để biết khi nào mảng đủ dùng.
Tài liệu đầy đủ nằm ở trang Map trên MDN. Hãy thử viết đoạn code đếm số lần xuất hiện của từng ký tự trong một chuỗi bằng Map. Bài tập nhỏ này bao phủ gần như mọi thao tác chính.



