Map<K, V>
Map là một trong những built-in API của JavaScript, là object để xử lý cặp key-value. Trong Map, một key chỉ có thể lưu trữ một giá trị duy nhất.
Cách tạo đối tượng Map
Để tạo đối tượng Map, dùng new với class Map. Ví dụ, để tạo Map<string, number> với key là string và value là number:
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map .get ("a"));
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map .get ("a"));
Khi truyền mảng [tuple type] [K, V][] là [K, V][] vào constructor, sẽ tạo object Map<K, V>.
📄️ Tuple
Function của TypeScript chỉ có thể trả về 1 giá trị. Tuy nhiên, thực tế có lúc muốn trả về nhiều giá trị. Trong trường hợp đó, có thể đặt tất cả giá trị muốn trả về vào mảng và return.
tsconstmap = newMap <string, number>([["a", 1],["b", 2],["c", 3],]);console .log (map );
tsconstmap = newMap <string, number>([["a", 1],["b", 2],["c", 3],]);console .log (map );
Nếu bỏ qua type variable của Map, TypeScript sẽ suy luận kiểu Map<K, V> từ tham số constructor.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);map ;
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);map ;
Nếu bỏ qua tham số constructor, sẽ tạo Map rỗng.
tsconstmap = newMap <string, number>();console .log (map );
tsconstmap = newMap <string, number>();console .log (map );
Nếu bỏ qua cả type argument và constructor argument, sẽ có kiểu Map<any, any>.
tsconstmap = newMap ();
tsconstmap = newMap ();
Type annotation cho Map
Khi type annotation cho Map trong TypeScript, chỉ định kiểu của phần tử Map vào type variable như Map<string, number>.
tsfunctiondoSomething (map :Map <string, number>) {}
tsfunctiondoSomething (map :Map <string, number>) {}
Key của Map được so sánh bằng strict equality
Việc key của Map có giống nhau hay không được xác định bằng strict equality (===). Không phải equality (==).
Ví dụ, null và undefined bằng nhau với equality, nhưng không bằng nhau với strict equality.
tsconsole .log (null ==undefined );console .log (null ===undefined );
tsconsole .log (null ==undefined );console .log (null ===undefined );
Do đó, Map coi null và undefined là các key khác nhau.
tsconstmap = newMap <any, any>([[null, 1]]);console .log (map .has (null));console .log (map .has (undefined ));
tsconstmap = newMap <any, any>([[null, 1]]);console .log (map .has (null));console .log (map .has (undefined ));
NaN với nhau không bằng nhau với strict equality, nhưng ngoại lệ được coi là cùng key.
js// JavaScriptconsole .log (NaN ===NaN );
js// JavaScriptconsole .log (NaN ===NaN );
tsconstmap = newMap <number, number>();map .set (NaN , 1);map .set (NaN , 2);console .log (map );
tsconstmap = newMap <number, number>();map .set (NaN , 1);map .set (NaN , 2);console .log (map );
Object không bằng nhau với cả equality và strict equality nên được coi là key khác nhau.
js// JavaScriptconsole .log ({} == {});console .log ({} === {});
js// JavaScriptconsole .log ({} == {});console .log ({} === {});
tsconstmap = newMap <object, number>();map .set ({}, 1);map .set ({}, 2);console .log (map );
tsconstmap = newMap <object, number>();map .set ({}, 1);map .set ({}, 2);console .log (map );
Thao tác với Map
Set phần tử - Map.prototype.set()
Để thêm cặp key-value vào Map, sử dụng method set.
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map );
tsconstmap = newMap <string, number>();map .set ("a", 1);console .log (map );
Nếu key đã tồn tại, sẽ ghi đè giá trị.
tsconstmap = newMap ([["a", 1]]);map .set ("a", 5);console .log (map );
tsconstmap = newMap ([["a", 1]]);map .set ("a", 5);console .log (map );
Lấy giá trị - Map.prototype.get()
Để lấy phần tử từ Map dựa trên key, sử dụng method get.
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("a"));
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("a"));
Method get trả về undefined nếu key không tồn tại.
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b"));
Kết hợp với toán tử Null coalescing có thể gán giá trị mặc định khi không lấy được giá trị bằng method get.
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b") ?? 2);
tsconstmap = newMap ([["a", 1]]);console .log (map .get ("b") ?? 2);
Xóa phần tử cụ thể - Map.prototype.delete()
Để xóa phần tử khỏi Map bằng cách chỉ định key, sử dụng method delete.
tsconstmap = newMap ([["a", 1],["b", 2],]);map .delete ("a");console .log (map );
tsconstmap = newMap ([["a", 1],["b", 2],]);map .delete ("a");console .log (map );
Giá trị trả về của delete là true nếu key tồn tại, ngược lại là false.
tsconstmap = newMap ([["a", 1]]);console .log (map .delete ("a"));console .log (map .delete ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .delete ("a"));console .log (map .delete ("b"));
Kiểm tra sự tồn tại của key - Map.prototype.has()
Để kiểm tra xem key có tồn tại trong Map hay không, sử dụng method has.
tsconstmap = newMap ([["a", 1]]);console .log (map .has ("a"));console .log (map .has ("b"));
tsconstmap = newMap ([["a", 1]]);console .log (map .has ("a"));console .log (map .has ("b"));
Code kiểm tra tồn tại bằng has rồi mới lấy phần tử bằng get không thể viết tốt trong TypeScript.
tsconstmap = newMap ([["a", 1]]);if (map .has ("a")) {// TypeScript không nhận biết có "a"constn =map .get ("a");'n' is possibly 'undefined'.18048'n' is possibly 'undefined'.* 2; n }
tsconstmap = newMap ([["a", 1]]);if (map .has ("a")) {// TypeScript không nhận biết có "a"constn =map .get ("a");'n' is possibly 'undefined'.18048'n' is possibly 'undefined'.* 2; n }
Trong trường hợp này, lấy giá trị bằng get rồi kiểm tra giá trị đó khác undefined sẽ hoạt động tốt.
tsconstmap = newMap ([["a", 1]]);constn =map .get ("a");if (typeofn === "number") {n * 2;}
tsconstmap = newMap ([["a", 1]]);constn =map .get ("a");if (typeofn === "number") {n * 2;}
Lấy số lượng phần tử - Map.prototype.size()
Để kiểm tra số lượng phần tử đã đăng ký trong Map, xem giá trị của field size.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );
Xóa tất cả phần tử - Map.prototype.clear()
Để xóa tất cả phần tử đã đăng ký trong Map, sử dụng method clear.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );map .clear ();console .log (map .size );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (map .size );map .clear ();console .log (map .size );
Liệt kê key - Map.prototype.keys()
Method keys trả về iterable object của các key.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeys = [...map .keys ()];console .log (keys );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeys = [...map .keys ()];console .log (keys );
Liệt kê value - Map.prototype.values()
Method values trả về iterable object của các giá trị.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constvalues = [...map .values ()];console .log (values );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constvalues = [...map .values ()];console .log (values );
Liệt kê cặp key-value - Map.prototype.entries()
Method entries trả về iterable object của key và value.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map .entries ()];console .log (keyValues );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map .entries ()];console .log (keyValues );
Lặp qua các cặp key-value
Map có thể lặp bằng for...of. Thứ tự lặp là theo thứ tự đã đăng ký.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);for (const [key ,value ] ofmap ) {console .log (key ,value );// Output theo thứ tự "a", 1// "b", 2// "c", 3}
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);for (const [key ,value ] ofmap ) {console .log (key ,value );// Output theo thứ tự "a", 1// "b", 2// "c", 3}
Sao chép
Để sao chép (shallow copy) object Map, truyền object Map vào constructor Map.
tsconstmap1 = newMap ([["a", 1]]);constmap2 = newMap (map1 );console .log (map2 );
tsconstmap1 = newMap ([["a", 1]]);constmap2 = newMap (map1 );console .log (map2 );
Map không thể chuyển trực tiếp thành JSON
Khi đưa object Map qua JSON.stringify, các phần tử đã đăng ký sẽ không trở thành JSON.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (JSON .stringify (map ));
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);console .log (JSON .stringify (map ));
Khi muốn chuyển Map thành JSON, cần chuyển thành object trước.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (JSON .stringify (obj ));
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (JSON .stringify (obj ));
Tương tác với các kiểu khác
Chuyển Map thành mảng
Sử dụng spread syntax với Map<K, V> sẽ được mảng tuple type [K, V][].
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map ];console .log (keyValues );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constkeyValues = [...map ];console .log (keyValues );
Chuyển object thành Map
Để chuyển object thành Map, truyền giá trị trả về của Object.entries vào constructor Map.
tsconstobj = {a : 1,b : 2,c : 3 };constmap = newMap (Object .entries (obj ));console .log (map );
tsconstobj = {a : 1,b : 2,c : 3 };constmap = newMap (Object .entries (obj ));console .log (map );
Chuyển Map thành object
Để chuyển Map thành object, truyền object Map vào Object.fromEntries.
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (obj );
tsconstmap = newMap ([["a", 1],["b", 2],["c", 3],]);constobj =Object .fromEntries (map );console .log (obj );
Sự khác biệt giữa Map và object
Về điểm có thể biểu diễn cặp key-value, Map và object tương tự nhau, nhưng có các điểm khác biệt sau:
| Điểm khác biệt | Map | Object |
|---|---|---|
| Ghi đè prototype key | Không xảy ra | Có thể xảy ra |
| Kiểu có thể dùng làm key | Bất kỳ kiểu nào | string hoặc symbol |
| Thứ tự lặp | Theo thứ tự chèn | Logic phức tạp |
| Chuyển thành JSON | Không thể trực tiếp | Trực tiếp được |
Ghi đè prototype key
Object có thể ghi đè key của prototype.
jsconstobj = {};console .log (obj .toString );obj .toString = 1;console .log (obj .toString );
jsconstobj = {};console .log (obj .toString );obj .toString = 1;console .log (obj .toString );
Map không lo ngại ghi đè key của prototype khi set phần tử. Bởi vì phần tử và prototype ở các vùng riêng biệt.
tsconstmap = newMap <string, any>();console .log (map .toString );map .set ("toString", 1);console .log (map .toString );
tsconstmap = newMap <string, any>();console .log (map .toString );map .set ("toString", 1);console .log (map .toString );
Kiểu có thể dùng làm key
Kiểu có thể dùng làm key của object là kiểu string hoặc kiểu symbol. Map có thể dùng bất kỳ kiểu nào làm key.
Thứ tự lặp
Thứ tự lặp qua property của object không phải theo thứ tự viết hoặc thêm vào, mà theo logic phức tạp.
📄️ Cách loop object
Giải thích cách loop qua property của object trong JavaScript/TypeScript.
Thứ tự lặp qua phần tử của Map được đảm bảo theo thứ tự thêm phần tử.
Chuyển thành JSON
Object có thể chuyển trực tiếp thành JSON bằng JSON.stringify. Map khi JSON.stringify thì phần tử không trở thành JSON. Cần chuyển Map thành object trước.
So sánh cách viết Map và object
Map và object có thể thực hiện các thao tác tương tự. Dưới đây là bảng tương ứng:
Map | Object | |
|---|---|---|
| Cách viết type annotation | Map<K, V> | Record<K, V> |
| Khởi tạo | new Map([["a", 1]]) | { a: 1 } |
| Set phần tử | map.set(key, value) | obj[key] = value |
| Lấy giá trị | map.get(key) | obj[key] |
| Xóa phần tử | map.delete(key) | delete obj.key |
| Kiểm tra key | map.has(key) | key in obj |
| Lấy số lượng phần tử | map.size | Object.keys(obj).length |
| Xóa tất cả phần tử | map.clear() | - |
| Liệt kê key | map.keys() | Object.keys(obj) |
| Liệt kê value | map.values() | Object.values(obj) |
| Liệt kê phần tử | map.entries() | Object.entries(obj) |
| Sao chép | new Map(map) | { ...obj } |
📄️ Record<Keys, Type>
Tạo object type từ key-value
Chia sẻ kiến thức
Map là built-in API của JS để xử lý cặp key-value
Type annotation trong TypeScript: Map<string, number>
Key được so sánh bằng strict equality
Map không thể chuyển trực tiếp thành JSON
Sự khác biệt giữa Map và object
→ Map có thể dùng bất kỳ kiểu nào làm key
→ Map đảm bảo thứ tự key theo thứ tự chèn
Từ 『Survival TypeScript』