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 cuộc sống thực. Đó là lý do MapSet tồn tại.

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à Mapcho phép các kiểu khóa khác nhau.

Phương thức và tính chất 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, undefinednếu keykhông tồn tại trên map.
  • map.has(key)– trả về truenếu keytồn tại, falsenế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ụ:

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 maplà 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ó khóa đối tượng, v.v.).

Vì vậy, chúng ta nên sử dụng map với phương thức: set, getvà 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 Mapvà quan trọng nhất . Đối với các chuỗi khóa, Objectcó 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

visitsCountObjmột đối tượng, nó chuyển đổi tất cả các khóa, chẳng hạn như johnthà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 Mapso sánh các key

Để kiểm tra các khóa tương đương, Mapsử 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, NaNcó 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.=

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');

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ới for..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. Mapduy trì trật tự này, không giống như một thông thường Object.

Bên cạnh đó, Mapcó 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
});

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 Maptừ đó, 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.entriestrả về mảng các cặp khóa / giá trị : [ ["name","John"], ["age", 30] ]. Đó là những gì Mapcần.

Object.fromEntries: Đối tượng từ Map

Chúng ta vừa thấy cách tạo Maptừ 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:

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.fromEntriesmong 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 tiêu chuẩn để maptrả 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.

Set

Một Setlà 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ượng iterable đượ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ề truenếu valuetồn tại tại thời điểm lệnh gọi, nếu không là false.
  • set.has(value)– trả về truenế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 Setlầ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 đó:

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ế Setcó 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ó.

Lặp lại trên Set

Chúng ta có thể lặp set với for..ofhoặ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 forEachcó 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 Mapkhi hàm gọi lại forEachcó ba đối số. Nhưng có thể giúp thay thế Mapvớ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ự Mapdà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ới Map,
  • 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ới Map.

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à tính chất:

  • new Map([iterable])– tạo map, với tùy chọn iterable(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, undefinednếu keykhông tồn tại trên map.
  • map.has(key)– trả về truenếu keytồn tại, falsenế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à tính chất:

  • new Set([iterable])– tạo set, với tùy chọn iterable(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ếu value đã tồn tại), trả về chính tập hợp đó.
  • set.delete(value)– loại bỏ giá trị, trả về truenếu valuetồn tại tại thời điểm gọi, nếu không là false.
  • set.has(value)– trả về truenế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 MapSetluô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ó.