Bây giờ chúng ta đã tìm hiểu về các cấu trúc dữ liệu phức tạp sau:
- Đối tượng để lưu trữ các bộ sưu tập khóa.
- Mảng để lưu trữ các bộ sưu tập theo thứ tự.
Nhưng điều đó là không đủ cho sử dụng thực tế. Đó là lý do Map
và Set
tồn tại.
Nội dung chính
1. Map
Map là một tập hợp các mục dữ liệu chứ các khóa(key), giống như một Object
. Nhưng sự khác biệt chính là Map
cho phép các kiểu khóa khác nhau.
Phương thức và thuộc tính là:
new Map()
– tạo Map.map.set(key, value)
– lưu trữ giá trị bằng khóa.map.get(key)
– trả về giá trị theo khóa,undefined
nếukey
không tồn tại trên map.map.has(key)
– trả vềtrue
nếukey
tồn tại,false
nếu không.map.delete(key)
– loại bỏ giá trị bằng khóa.map.clear()
– loại bỏ mọi thứ khỏi map.map.size
– trả về số phần tử hiện tại.
Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
let map = new Map();
map.set('1', 'str1'); // a string key
map.set(1, 'num1'); // a numeric key
map.set(true, 'bool1'); // a boolean key
// remember the regular Object? it would convert keys to string
// Map keeps the type, so these two are different:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.size ); // 3
Như chúng ta có thể thấy, không giống như các đối tượng, các khóa không được chuyển đổi thành chuỗi. Bất kỳ kiểu chìa khóa nào cũng có thể dùng trong map.map[key]
không phải là cách đúng để sử dụng Map
Mặc dù map[key]
cũng hoạt động, ví dụ chúng ta có thể thiết lập map[key] = 2
, điều này được coi map
là một đối tượng JavaScript đơn giản, do đó, nó bao hàm tất cả các giới hạn tương ứng (không có đối tượng khóa, v.v.).
Vì vậy, chúng ta nên sử dụng map
với phương thức: set
, get
và vân vân.
Map cũng có thể sử dụng các đối tượng làm khóa(key).
Ví dụ:
let john = { name: "John" };
// for every user, let's store their visits count
let visitsCountMap = new Map();
// john is the key for the map
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
Sử dụng các đối tượng làm khóa là một trong những tính năng đáng chú ý của Map
và quan trọng nhất . Đối với các chuỗi khóa, Object
có thể tốt, nhưng không phải cho các khóa đối tượng.
Hãy thử:
let john = { name: "John" };
let visitsCountObj = {}; // try to use an object
visitsCountObj[john] = 123; // try to use john object as the key
// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123
Là visitsCountObj
một đối tượng, nó chuyển đổi tất cả các khóa, chẳng hạn như john
thành chuỗi, vì vậy chúng ta đã có khóa chuỗi "[object Object]"
. Chắc chắn không phải là những gì chúng ta muốn.
Làm thế nào để dùng Map
so sánh các key
Để kiểm tra các khóa tương đương, Map
sử dụng thuật toán SameValueZero. Nó gần giống như bình đẳng nghiêm ngặt ===
, nhưng sự khác biệt là NaN
được coi là bằng NaN
. Vì vậy, NaN
có thể được sử dụng như là key khá tốt.
Thuật toán này không thể thay đổi hoặc tùy chỉnh. với =
Xâu chuỗi
Mỗi map.set
gọi trả về chính bản thân nó, vì vậy chúng ta có thể một chuỗi các cuộc gọi:
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
2. Lặp lại trên Map
Để lặp qua một map
, có 3 phương thức:
map.keys()
– trả về một lần lặp cho các khóa,map.values()
– trả về một lần lặp cho các giá trị,map.entries()
– trả về một lần lặp cho các mục[key, value]
, nó được sử dụng vớifor..of
.
Ví dụ:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// iterate over keys (vegetables)
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// iterate over values (amounts)
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// iterate over [key, value] entries
for (let entry of recipeMap) { // the same as of recipeMap.entries()
alert(entry); // cucumber,500 (and so on)
}
Thứ tự chèn trong Map
Lặp lại theo thứ tự như các giá trị được chèn vào. Map
duy trì trật tự này, không giống như một Object
thông thường.
Bên cạnh đó, Map
có một phương thức làforEach
, tương tự như Array
:
// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 500 etc
});
3. Object.entries: Map từ Object
Khi một Map
được tạo, chúng ta có thể truyền cho nó một mảng với các cặp khóa / giá trị để khởi tạo, như thế này:
// array of [key, value] pairs
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
alert( map.get('1') ); // str1
Nếu chúng ta có một đối tượng đơn giản và chúng ta muốn tạo một đối tượng Map
từ đó, thì chúng ta có thể sử dụng phương thức Object.entries(obj) để trả về một mảng các cặp khóa / giá trị cho một đối tượng chính xác theo định dạng đó.
Vì vậy, chúng ta có thể tạo map từ một đối tượng như thế này:
let obj = {
name: "John",
age: 30
};
let map = new Map(Object.entries(obj));
alert( map.get('name') ); // John
Ở đây, Object.entries
trả về mảng các cặp khóa / giá trị : [ ["name","John"], ["age", 30] ]
. Đó là những gì Map
cần.
4. Object.fromEntries: Đối tượng từ Map
Chúng ta vừa thấy cách tạo Map
từ một đối tượng đơn giản với Object.entries(obj)
.
Có phương thức Object.fromEntries
thực hiện ngược lại: đưa ra một loạt các cặp [key, value]
và tạo ra một đối tượng từ chúng:
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// now prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2
Chúng ta có thể sử dụng Object.fromEntries
để có được một đối tượng đơn giản từ Map
.
Ví dụ: chúng ta lưu trữ dữ liệu trong một Map
, nhưng chúng tôi cần chuyển nó sang code của bên thứ 3 mong đợi một đối tượng đơn giản.
Ở đây chúng ta sẽ làm:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
let obj = Object.fromEntries(map.entries()); // make a plain object (*)
// done!
// obj = { banana: 1, orange: 2, meat: 4 }
alert(obj.orange); // 2
Một lệnh gọi để map.entries()
trả về một mảng các cặp khóa / giá trị, chính xác theo đúng định dạng cho Object.fromEntries
.
Chúng ta cũng có thể làm cho dòng (*)
ngắn hơn:
let obj = Object.fromEntries(map); // omit .entries()
Điều đó cũng tương tự, vì Object.fromEntries
mong muốn một đối tượng có thể lặp lại làm đối số. Không nhất thiết là một mảng. Và phép lặp chuẩn để map
trả về các cặp khóa / giá trị giống như map.entries()
. Vì vậy, chúng ta nhận được một đối tượng đơn giản với cùng khóa / giá trị như map
.
5. Set
Một Set
là một bộ sưu tập kiểu đặc biệt – Tập hợp các giá trị (không có key), trong đó mỗi giá trị chỉ có thể xuất hiện một lần trong một set.
Phương thức chính của nó là:
new Set(iterable)
– tạo tập hợp và nếu một đối tượngiterable
được cung cấp (thường là một mảng), sao chép các giá trị từ nó vào tập hợp.set.add(value)
– thêm một giá trị, trả về tập hợp của chính nó.set.delete(value)
– loại bỏ giá trị, trả vềtrue
nếuvalue
tồn tại tại thời điểm lệnh gọi, nếu không làfalse
.set.has(value)
– trả vềtrue
nếu giá trị tồn tại trong tập hợp, nếu không làfalse
.set.clear()
– loại bỏ mọi thứ khỏi set.set.size
– đếm các thành phần của set.
Tính năng chính là các lệnh gọi lặp đi lặp lại set.add(value)
với cùng một giá trị nhưng giá trị đó sẽ không được add vào set nếu nó đã tồn tại. Đó là lý do tại sao mỗi giá trị xuất hiện chỉ trong một Set
lần.
Ví dụ: chúng ta có khách đến thăm và chúng ta muốn nhớ hết mọi người đến thăm. Nhưng các chuyến thăm lặp đi lặp lại và không nên nhớ trùng lặp người tới thăm. Một khách truy cập phải được lưu lại một lần.
Set
có thể làm được điều đó:
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// visits, some users come multiple times
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set keeps only unique values
alert( set.size ); // 3
for (let user of set) {
alert(user.name); // John (then Pete and Mary)
}
Lựa chọn thay thế Set
có thể là một mảng các người dùng và code để kiểm tra các bản sao trên mỗi lần chèn bằng cách sử dụng arr.find. Nhưng hiệu suất sẽ tồi tệ hơn nhiều, bởi vì phương thức này đi qua toàn bộ mảng và kiểm tra mọi phần tử. Set
được tối ưu hóa tốt hơn nhiều trong nội bộ để kiểm tra tính duy nhất của các phần tử của trong nó.
6. Lặp lại trên Set
Chúng ta có thể lặp set với for..of
hoặc sử dụng forEach
:
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) alert(value);
// the same with forEach:
set.forEach((value, valueAgain, set) => {
alert(value);
});
Lưu ý. Hàm gọi lại(callback) được truyền vào forEach
có 3 đối số: một value
, sau đó cùng với một giá trị valueAgain
và sau đó là đối tượng đích. Thật vậy, cùng một giá trị xuất hiện trong các đối số hai lần.
Đó là vì tính tương thích với Map
khi hàm gọi lại forEach
có ba đối số. Nhưng có thể giúp thay thế Map
với Set
trong các trường hợp nhất định một cách dễ dàng, và ngược lại.
Các phương thức tương tự Map
dành cho các vòng lặp cũng được hỗ trợ:
set.keys()
– trả về một đối tượng có thể lặp các giá trị,set.values()
– giống nhưset.keys()
, để tương thích vớiMap
,set.entries()
– trả về một đối tượng lặp lại cho các mục[value, value]
, tồn tại để tương thích vớiMap
.
7. Tóm lược
Map
– là một tập hợp các giá trị với mỗi giá trị có một khoá.
Phương thức và thuộc tính:
new Map([iterable])
– tạo map, với tùy chọniterable
(ví dụ mảng) có các cặp[key,value]
để khởi tạo.map.set(key, value)
– lưu trữ giá trị bằng khóa.map.get(key)
– trả về giá trị theo khóa,undefined
nếukey
không tồn tại trên map.map.has(key)
– trả vềtrue
nếukey
tồn tại,false
nếu không.map.delete(key)
– loại bỏ giá trị bằng khóa.map.clear()
– loại bỏ mọi thứ khỏi map.map.size
– trả về số lượng phần tử hiện tại.
Sự khác biệt với Object
:
- Bất kỳ khóa, đối tượng có thể dùng làm khóa(key).
- Có những phương thức thuận tiện, thuộc tính
size
.
Set
– là một tập hợp các giá trị.
Phương thức và thuộc tính:
new Set([iterable])
– tạo set, với tùy chọniterable
(ví dụ mảng) có giá trị để khởi tạo.set.add(value)
– thêm một giá trị (không làm gì nếuvalue
đã tồn tại), trả về chính tập hợp đó.set.delete(value)
– loại bỏ giá trị, trả vềtrue
nếuvalue
tồn tại tại thời điểm gọi, nếu không làfalse
.set.has(value)
– trả vềtrue
nếu giá trị tồn tại trong tập hợp, nếu không làfalse
.set.clear()
– loại bỏ mọi thứ khỏi set.set.size
– là số lượng mục hay phần tử trong set.
Việc lặp đi lặp lại Map
và Set
luôn theo thứ tự chèn, vì vậy chúng ta không thể nói rằng các bộ sưu tập này không có thứ tự, nhưng chúng ta không thể sắp xếp lại các phần tử hoặc trực tiếp lấy một phần tử theo số của nó.
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!