Đối tượng `Map`

Bài viết này giải thích về đối tượng Map.

Chúng tôi sẽ giải thích từng bước, từ các thao tác cơ bản đến các ví dụ thực tế hữu ích trong các tình huống hàng ngày.

YouTube Video

Đối tượng Map

Map là một bộ sưu tập lưu trữ các cặp khóa-giá trị. Nó giống như một đối tượng, nhưng khác ở chỗ bất kỳ kiểu nào, chẳng hạn như đối tượng, hàm hoặc kiểu nguyên thủy, đều có thể được sử dụng làm khóa và thứ tự chèn được giữ nguyên.

Cơ bản về Map

Trước tiên, hãy xem cách tạo một Map và thực hiện các thao tác cơ bản.

Đoạn mã sau là một ví dụ tối giản tạo một map rỗng, thêm các khóa và lấy giá trị.

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • Trong đoạn mã này, bạn có thể thêm phần tử bằng set, lấy giá trị bằng get và kiểm tra số lượng phần tử với thuộc tính size.
  • Map giữ nguyên thứ tự chèn, phù hợp cho các xử lý phụ thuộc vào thứ tự.

Cách hoạt động của set, get, has, và delete

Dưới đây là các ví dụ về thao tác đọc, ghi, kiểm tra tồn tại và xoá điển hình.

Với đoạn mã sau, bạn có thể kiểm tra giá trị trả về và tác động của từng phương thức.

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • get trả về undefined nếu khóa không tồn tại. has kiểm tra sự tồn tại của một khóa, còn delete xóa một khóa.
  • Ngoài ra, vì set trả về chính map nên có thể sử dụng phương pháp nối chuỗi (chaining).

Bất kỳ kiểu dữ liệu nào cũng có thể dùng làm khóa (sử dụng đối tượng làm khóa)

Một trong những ưu điểm chính của Map là bạn có thể sử dụng các đối tượng trực tiếp làm khóa.

Ví dụ sau cho thấy cách liên kết giá trị trong Map sử dụng đối tượng làm khóa.

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • Khi sử dụng đối tượng làm khóa, điều quan trọng là chúng phải có cùng tham chiếu. Ngay cả khi nội dung giống nhau, các đối tượng có tham chiếu khác nhau vẫn được coi là khác và không phải là cùng một khóa.

Lặp qua (Iteration)

Map giữ thứ tự chèn nên thường được sử dụng để lặp qua.

Dưới đây, chúng tôi sẽ chỉ cách sử dụng for...of, forEach và các phương thức keys(), values()entries().

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries() trả về một mảng các cặp [key, value], có thể chuyển thành mảng bằng cú pháp spread. Lưu ý rằng hàm callback của forEach nhận các tham số theo thứ tự value, key.

Chuyển đổi giữa MapObject

Bạn có thể chuyển đổi một object hiện có thành Map, hoặc chuyển Map thành object thông thường hoặc mảng.

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • Sử dụng Object.entriesObject.fromEntries giúp việc chuyển đổi dễ dàng. Tuy nhiên, do khóa của object chỉ là chuỗi hoặc symbol nên khi chuyển đổi về object, các khóa không phải chuỗi sẽ bị mất.

Mẫu thực tế: Đếm tần suất (Count Map)

Khi đếm tần suất các phần tử trong một mảng, việc sử dụng Map giúp quá trình này trở nên đơn giản hơn.

Đoạn mã sau đây là ví dụ sử dụng map để đếm tần suất chuỗi trong mảng và sắp xếp chúng.

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • Sử dụng Map giúp việc kiểm tra sự tồn tại và cập nhật trở nên dễ dàng hơn. Điều này cũng có thể thực hiện với object, nhưng Map sẽ trực quan hơn khi làm việc với các khóa bất kỳ.

Lưu ý về MapJSON.stringify (tuần tự hóa dữ liệu)

JSON.stringify không thể tuần tự hóa trực tiếp một Map. Nếu bạn cần lưu một Map, bạn phải chuyển đổi nó trước.

Ví dụ sau đây cho thấy cách chuyển đổi một Map thành mảng trước khi biến nó thành JSON và cách khôi phục lại.

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • Các Map cần lưu trữ hoặc truyền đi nên được chuyển thành mảng trước khi tuần tự hóa. Khi khôi phục, sử dụng JSON.parse để chuyển thành mảng, sau đó đổi lại thành Map.

Giới thiệu về WeakMap và cách sử dụng

WeakMap khác ở chỗ các khóa của nó được tham chiếu yếu (có thể bị thu gom bộ nhớ).

Nó hữu ích để giữ bộ nhớ cache hoặc siêu dữ liệu với đối tượng làm khóa, tự động giải phóng khi đối tượng khóa bị thu gom bộ nhớ.

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap không thể lặp qua hoặc kiểm tra kích thước, nhưng rất hữu ích để ngăn chặn rò rỉ bộ nhớ.

Tóm tắt

Map là một bộ sưu tập tiện lợi, khác với object ở chỗ cho phép mọi kiểu dữ liệu làm khóa và giữ thứ tự chèn. Bằng cách hiểu từ thao tác cơ bản đến nâng cao, bạn có thể quản lý dữ liệu linh hoạt và trực quan hơn. Bằng cách sử dụng ObjectMap một cách phù hợp tùy từng trường hợp, bạn có thể nâng cao đáng kể tính rõ ràng và khả năng đọc của mã.

Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.

YouTube Video