Hôm nay chúng ta sẽ học về một phần khá thú vị giúp thuận tiện hơn trong quá trình làm việc với dữ liệu. Đó là Map và Set trong JavaScript. Cho đến bây giờ chúng ta đã học về các kiểu dữ liệu phức tạp như:
- Objects được sử dụng để lưu trữ dữ liệu có khóa (key)
- Arrays được sử dụng để lưu trữ dữ liệu có thứ tự.
Nhưng điều đó là chưa đủ, và đó là lý do tại sao map và set trong JavaScript tồn tại.
Map trong JavaScript
Map là tập hợp các dữ liệu có khóa, giống như Object
. Nhưng điểm khác nhau chính đó là Map cho phép các key có các kiểu dữ liệu khác nhau.
Map có các phương thức và thuộc tính như sau:
-
new Map()
– dùng để tạo mới Map -
map.set(key, value)
– dùng để lưu trữ một giá trị với key -
map.get(key)
– trả về giá trị theo key, undefine nếu key không tồn tại trong map. -
map.has(key)
– trả vềtrue
nếukey
tồn tại trong Map,false
nếu ngược lại -
map.delete(key)
– Xóa giá trị theo key -
map.clear()
– Xóa mọi thứ khỏi Map -
map.size
– Trả về số lượng phần tử trong Map
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 |
let map = new Map(); map.set('1', 'str1'); // key kiểu string map.set(1, 'num1'); // key kiểu number map.set(true, 'bool1'); // key kiểu boolean // Bạn còn nhớ về Object không? Nó sẽ chuyển các key sang kiểu string // Map sẽ giữ nguyên kiểu dữ liệu của key, vì vậy 2 điều này khác nhau: alert( map.get(1) ); // 'num1' alert( map.get('1') ); // 'str1' alert( map.size ); // 3 |
Như bạn thấy, không giống như Object, các key sẽ không chuyển sang kiểu string. Key có kiểu dữ liệu nào cũng được.
Lưu ý:
map[key] là cách sử dụng không đúng khi dùng Map. Mặc dù map[key]
vẫn sẽ làm việc. Ví dụ chúng ta có thể set map[key] = 2
, điều này đang xem Map
như một Object thông thường trong JavaScript. Vì vậy nếu bạn sử dụng cách này bạn sẽ bị giới hạn trong sử dụng Object (ví dụ không đặt được key với kiểu boolean…).
Vì vậy nên sử dụng các phương thức của map
: set
, get
…
Map cũng có thể sử dụng Object như key của nó
Ví dụ:
1 2 3 4 5 6 7 8 9 |
let john = { name: "John" }; // Đối với mỗi user, hãy lưu số lần truy cập let visitsCountMap = new Map(); // john là key của map visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123 |
Sử dụng Object như một key là một tính năng quan trọng trong Map
. Điều này sẽ không khả dụng trong Object. Object chỉ có thể sử dụng kiểu string để làm key thôi chứ không sử dụng Object khác để làm key cho nó đâu nhé.
Hãy thử xem nào:
1 2 3 4 5 6 7 8 9 |
let john = { name: "John" }; let ben = { name: "Ben" }; let visitsCountObj = {}; // Thử sử dụng Object visitsCountObj[ben] = 234; // Thử sử dụng ben Object như một key visitsCountObj[john] = 123; // Thử sử dụng john Object như một key alert( visitsCountObj["[object Object]"] ); // 123 |
visitsCountObj
là một Object, nó sẽ chuyển tất cả key
của nó như john
và ben
ở trên thành một chuỗi "[object Object]"
. Tất nhiên đây không phải là những gì chúng ta muốn.
Để kiểm tra sự tương đồng của các key, Map sử dụng thuật toán SameValueZero. Nó gần giống như toán tử ===, nhưng sự khác biệt là NaN được coi là ngang bằng với NaN. Vì vậy, NaN cũng có thể được sử dụng làm key.
Không thể thay đổi hoặc tùy chỉnh thuật toán này.
Mỗi lệnh gọi map.set
sẽ trả về chính nó, nên chúng ta có thể viết thành một chuỗi liền nhau như sau:
1 2 3 |
map.set('1', 'str1') .set(1, 'num1') .set(true, 'bool1'); |
Vòng lặp với Map
Để lặp Map, chúng ta có thể sử dụng 3 phương thức:
-
map.keys()
– Trả về key có thể lặp lại -
map.values()
– Trả về giá trị có thể lặp lại -
map.entries()
– Trả về một mảng[key, value]
, nó được sử dụng mặc địnhfor..of
.
Ví dụ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
let recipeMap = new Map([ ['cucumber', 500], ['tomatoes', 350], ['onion', 50] ]); // Lặp các key for (let vegetable of recipeMap.keys()) { alert(vegetable); // cucumber, tomatoes, onion } // Lặp giá trị for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } // Lặp [key, value] for (let entry of recipeMap) { // the same as of recipeMap.entries() alert(entry); // cucumber,500 } |
Việc lặp đi lặp lại theo thứ tự giống như các giá trị đã được chèn vào. Map
bảo toàn thứ 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 forEach được tích hợp sẵn, tương tự như Array:
1 2 3 4 5 6 7 8 9 |
let recipeMap = new Map([ ['cucumber', 500], ['tomatoes', 350], ['onion', 50] ]); recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc }); |
Khởi tạo Map
Khi một Map
được tạo, chúng ta có thể truyền vào một mảng với các cặp key/value để khởi tạo, như sau:
1 2 3 4 5 6 7 8 |
// array với cặp [key, value] let map = new Map([ ['1', 'str1'], [1, 'num1'], [true, 'bool1'] ]); alert( map.get('1') ); // str1 |
Nếu chúng ta có một Object đơn giản, và muốn khởi tạo map
từ Object đó. Chúng ta có thể sử dụng phương thức Object.entries(obj) , phương thức này sẽ trả về một mảng với cặp key|value từ Object được truyền vào.
Vì vậy chúng ta có thể tạo Map
từ một Object như sau:
1 2 3 4 5 6 7 8 |
let obj = { name: "John", age: 30 }; let map = new Map(Object.entries(obj)); alert( map.get('name') ); // John |
Ở đây, Object.entries
sẽ trả về một mảng [ ["name","John"], ["age", 30] ]
. Và đó là những gì mà Map
cần để khởi tạo.
Tạo Object từ Map
Chúng ta vừa học cách tạo Map
từ một Object đơn giản với Object.entries (obj)
. Phương thức Object.fromEntries
thực hiện ngược lại điều đó: cho một mảng các cặp [key, value], nó sẽ tạo ra một đối tượng từ chúng:
1 2 3 4 5 6 7 8 9 |
let prices = Object.fromEntries([ ['banana', 1], ['orange', 2], ['meat', 4] ]); // prices = { banana: 1, orange: 2, meat: 4 } alert(prices.orange); // 2 |
Chúng ta có thể sử dụng Object.fromEntries
để get một Object từ Map
. Ví dụ. chúng ta lưu trữ dữ liệu trong Map
, nhưng chúng ta cần chuyển dữ liệu đó sang mã của bên thứ 3, bên thứ 3 yêu cầu một Object.
1 2 3 4 5 6 7 8 9 10 11 |
let map = new Map(); map.set('banana', 1); map.set('orange', 2); map.set('meat', 4); let obj = Object.fromEntries(map.entries()); // get một object (*) // done! // obj = { banana: 1, orange: 2, meat: 4 } alert(obj.orange); // 2 |
Một lệnh gọi tới map.entries ()
trả về một cặp key / value có thể lặp lại, chính xác ở định dạng phù hợp cho Object.fromEntries
. Chúng ta cũng có thể làm cho dòng (*) ngắn hơn:
1 |
let obj = Object.fromEntries(map); |
Vì Object.fromEntries
mong đợi một đối tượng có thể lặp lại làm đối số. Không nhất thiết phải là một mảng. Và phép lặp tiêu chuẩn cho map
trả về các cặp key / value 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ác key / value giống như map
.
Set trong JavaScript
Set là một tập hợp các giá trị không có key và không trùng nhau. Vì thế trong tập hợp giá trị sẽ là duy nhất. Đây cũng là điểm khác nhau giữa Map và Set trong JavaScript. Nó có các phương thức sau:
-
new Set(iterable)
– Khởi tạo set, nếu iterable được truyền vào (thường sẽ là array), nó sẽ sao chép giá trị của iterable vào set. -
set.add(value)
– Thêm một giá trị, sẽ trả về chính nó (set). -
set.delete(value)
– Xóa giá trị trong set. Trả về true nếu giá trị tồn tại trong set, ngược lại sẽ trả về false. -
set.has(value)
– Trả về true nếu giá trị tồn tại trong set, ngược lại sẽ trả về false. -
set.clear()
– Xóa mọi thứ trong set. -
set.size
– Trả về số phần tử trong set.
Đặc điểm chính là các lệnh gọi set.add (value)
lặp đi lặp lại với cùng một giá trị sẽ không có tác dụng gì. Đó là lý do tại sao mỗi giá trị chỉ xuất hiện trong Set
một lần.
Ví dụ: chúng ta có khách đến thăm và chúng ta muốn ghi nhớ mọi người. Nhưng việc truy cập lặp lại không được dẫn đến trùng lặp. Một khách truy cập chỉ được “tính” một lần.
Set
là điều phù hợp cho điều đó:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let set = new Set(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; // Khách đến thăm, có nhiều khách đến nhiều lần set.add(john); set.add(pete); set.add(mary); set.add(john); set.add(mary); // Chỉ lưu trữ các giá trị duy nhất alert( set.size ); // 3 for (let user of set) { alert(user.name); // John (sau đó là Pete và Mary) } |
Thay thế cho Set
có thể là một mảng người dùng và ID để kiểm tra các bản sao trên mỗi lần thêm vào bằng arr.find. Nhưng hiệu suất sẽ kém hơn nhiều, bởi vì phương pháp này sẽ lặp qua toàn bộ mảng để kiểm tra mọi phần tử. Set
được tối ưu hóa nội bộ tốt hơn nhiều để kiểm tra tính duy nhất.
Vòng lặp với Set
Chúng ta có thể lặp lại một Set
với for..of hoặc sử dụng forEach:
1 2 3 4 5 6 7 8 |
let set = new Set(["oranges", "apples", "bananas"]); for (let value of set) alert(value); // Tương tự với forEach: set.forEach((value, valueAgain, set) => { alert(value); }); |
Lưu ý điều này. Hàm gọi lại được truyền vào forEach có 3 đối số: một value
, sau đó là giá trị tương tự valueAgain
, và sau đó là Set
. Thật vậy, cùng một giá trị xuất hiện trong các đối số hai lần. Bởi vì Set
thì không có key.
Và còn để tương thích với Map
trong đó lệnh gọi lại được truyền cho forEach có ba đối số. Có vẻ hơi lạ, Nhưng nó có thể giúp dễ dàng thay thế Map
bằng Set
trong một số trường hợp nhất định và ngược lại.
Các phương thức tương tự mà Map
có cho các trình vòng lặp cũng được hỗ trợ:
-
set.keys()
– Trả về giá trị. -
set.values()
– Tương tự nhưset.keys()
, để tương thích vớiMap
-
set.entries()
– Trả về object với[value, value]
.
Kết luận
Cùng ôn lại những ý chính đã học về map và set trong JavaScript đã học trong bài hôm nay nhé.
Map
là tập hợp các giá trị với key.
Map
có các phương thức và thuộc tính như sau:
-
new Map()
– dùng để tạo mới Map -
map.set(key, value)
– dùng để lưu trữ một giá trị với key -
map.get(key)
– trả về giá trị theo key, undefine nếu key không tồn tại trong map. -
map.has(key)
– trả vềtrue
nếukey
tồn tại trong Map,false
nếu ngược lại -
map.delete(key)
– Xóa giá trị theo key -
map.clear()
– Xóa mọi thứ khỏi Map -
map.size
– Trả về số lượng phần tử trong Map
Map
khác với Object thông thường:
- Key có thể là bất kỳ kiểu dữ liệu nào, thậm chí Object cũng có thể sử dụng làm key
- Có thêm thuộc tính
size
để kiểm tra phần tử trongMap
.
Set
là tập hợp các giá trị duy nhất không trùng lặp và không có key.
Set
có các phương thức sau:
-
new Set(iterable)
– Khởi tạo set, nếu iterable được truyền vào (thường sẽ là array), nó sẽ sao chép giá trị của iterable vào set. -
set.add(value)
– Thêm một giá trị, sẽ trả về chính nó (set). -
set.delete(value)
– Xóa giá trị trong set. Trả về true nếu giá trị tồn tại trong set, ngược lại sẽ trả về false. -
set.has(value)
– Trả về true nếu giá trị tồn tại trong set, ngược lại sẽ trả về false. -
set.clear()
– Xóa mọi thứ trong set. -
set.size
– Trả về số phần tử trong set.