Set

Đối tượng Set cho phép bạn lưu trữ các giá trị duy nhất của bất kỳ loại nào, bất kể là giá trị nguyên thủy hay tham chiếu đối tượng.

Cú pháp

new Set([iterable]);

Tham số

Tham sốMô tả
iterableNếu truyền vào một đối tượng có thể lặp lại, tất cả các phần tử của nó sẽ được thêm vào Set mới. Nếu không có tham số này, Set mới sẽ rỗng.

Mô tả

Khi thêm giá trị vào Set, không có chuyển đổi kiểu dữ liệu xảy ra, vì vậy 5'5' được coi là hai giá trị khác nhau. Set sử dụng thuật toán Same-value equality để xác định xem hai giá trị có bằng nhau hay không, nó tương tự như toán tử so sánh chính xác (===), nhưng khác biệt chính là NaN được coi là bằng chính nó, trong khi toán tử so sánh chính xác coi NaN không bằng chính nó.

Đối tượng nguyên mẫu

Thuộc tính

Thuộc tínhMô tả
Set.prototype.constructorHàm tạo ra đối tượng, mặc định là hàm Set.
Set.prototype.sizeTrả về tổng số thành viên của đối tượng Set.

Phương thức

Các phương thức của đối tượng Set được chia thành hai loại chính: phương thức thao tác (được sử dụng để thao tác dữ liệu) và phương thức duyệt (được sử dụng để duyệt qua các thành viên).

Phương thức tương tác

Phương thứcMô tả
Set.prototype.add(value)Được sử dụng để thêm một giá trị cụ thể vào cuối một đối tượng Set.
Set.prototype.delete(value)Được sử dụng để xóa một phần tử cụ thể khỏi một đối tượng Set.
Set.prototype.has(value)Trả về một giá trị boolean để chỉ ra xem giá trị value tương ứng có tồn tại trong đối tượng Set hay không.
Set.prototype.clear()Xóa tất cả các phần tử trong một đối tượng Set.

Phương thức duyệt

Phương thứcMô tả
Set.prototype.keys()Tương tự như phương thức values(), trả về một đối tượng trình lặp mới chứa tất cả các giá trị của các phần tử trong đối tượng Set theo thứ tự chèn.
Set.prototype.values()Trả về một đối tượng trình lặp mới chứa tất cả các giá trị của các phần tử trong đối tượng Set theo thứ tự chèn.
Set.prototype.entries()Trả về một đối tượng trình lặp mới chứa các mục mới, trong đó mỗi mục là một mảng [value, value] tương tự nhau, trong đó value là mỗi phần tử của đối tượng Set. Mục đích của việc trả về một mảng [value, value] là để duy trì cấu trúc API tương tự với đối tượng Map, mặc dù trong trường hợp này, cả keyvalue đều có cùng giá trị.
Set.prototype.forEach()Dựa trên thứ tự các phần tử trong tập hợp, thực hiện một hàm gọi lại cho mỗi phần tử.

Cần lưu ý rằng, thứ tự duyệt qua các phần tử của Set chính là thứ tự chèn. Đặc điểm này đôi khi rất hữu ích, ví dụ như sử dụng Set để lưu trữ một danh sách các hàm gọi lại, việc gọi lại sẽ được đảm bảo theo thứ tự đã thêm vào.

Phương thức keysvalues trả về cùng một kết quả, đó là một trình lặp. Vì Set không có tên khóa, chỉ có giá trị (hoặc có thể nói tên khóa và giá trị là cùng một giá trị), nên hành vi của phương thức keysvalues hoàn toàn giống nhau.

Set.prototype.keys()

Được sử dụng để lấy tất cả các giá trị của các phần tử trong đối tượng Set theo thứ tự chèn.

let set = new Set(['x', 'y', 'z']);
 
for (let item of set.keys()) {
  console.log(item);
}
// Output: 'x'
// Output: 'y'
// Output: 'z'
Set.prototype.values()

Trả về một đối tượng trình lặp chứa tất cả các giá trị của các phần tử trong đối tượng Set theo thứ tự chèn.

let set = new Set(['x', 'y', 'z']);
 
for (let item of set.values()) {
  console.log(item);
}
// Output: 'x'
// Output: 'y'
// Output: 'z'
Set.prototype.entries()

Phương thức entries trả về một đối tượng trình lặp mới, trong đó mỗi mục là một mảng [value, value] tương tự nhau, trong đó value là mỗi phần tử của đối tượng Set. Mục đích của việc trả về một mảng [value, value] là để duy trì cấu trúc API tương tự với đối tượng Map, mặc dù trong trường hợp này, cả keyvalue đều có cùng giá trị.

let set = new Set(['x', 'y', 'z']);
 
for (let item of set.entries()) {
  console.log(item);
}
// Output: ["x", "x"]
// Output: ["y", "y"]
// Output: ["z", "z"]

Mặc định, đối tượng Set có thể được duyệt qua, và hàm tạo trình lặp mặc định của nó chính là phương thức values.

console.log(Set.prototype[Symbol.iterator] === Set.prototype.values); // Output: true

Điều này có nghĩa là có thể bỏ qua phương thức values và trực tiếp sử dụng vòng lặp for…of để duyệt qua Set.

let set = new Set(['x', 'y', 'z']);
 
for (let x of set) {
  console.log(x);
}
// Output: 'x'
// Output: 'y'
// Output: 'z'
Set.prototype.forEach()

Phương thức forEach của đối tượng Set được sử dụng để thực hiện một hàm gọi lại cho mỗi phần tử, không có giá trị trả về.

let set = new Set([1, 2, 3]);
 
set.forEach((value, key) => console.log(value * 2));
// Output: 2
// Output: 4
// Output: 6

Đoạn mã trên cho thấy, tham số của phương thức forEach chính là một hàm xử lý. Các tham số của hàm này lần lượt là giá trị khóa, tên khóa và bộ sưu tập chính nó (trong ví dụ trên đã bỏ qua tham số này). Ngoài ra, phương thức forEach còn có thể có một tham số thứ hai để chỉ định đối tượng this được ràng buộc.

Ví dụ

Cú pháp cơ bản

const s = new Set();
 
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
 
for (let i of s) {
  console.log(i);
}
// Output: 2 3 5 4

Cơ chế kiểm tra nội bộ

NaN bằng nhau

Đoạn mã dưới đây thêm hai giá trị NaN vào một thể hiện của Set, nhưng thực tế chỉ có thể thêm một giá trị. Điều này cho thấy rằng trong Set, hai giá trị NaN được coi là bằng nhau.

let set = new Set();
 
let a = NaN;
let b = NaN;
 
set.add(a);
set.add(b);
 
console.log(set); // Output: Set{NaN}

Đối tượng không bằng nhau

Đoạn mã dưới đây cho thấy rằng do hai đối tượng rỗng không bằng nhau, nên chúng được coi là hai giá trị riêng biệt.

let set = new Set();
 
set.add({});
set.size; // Output: 1
 
set.add({});
set.size; // Output: 2

Chuyển đổi sang mảng

Phương thức Array.from có thể chuyển đổi cấu trúc Set thành một mảng.

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

Loại bỏ các phần tử trùng lặp trong mảng

Điều này cung cấp một cách để loại bỏ các phần tử trùng lặp trong một mảng.

const dedupe = array => Array.from(new Set(array));
 
dedupe([1, 1, 2, 3]); // [1, 2, 3]

Ứng dụng của toán tử mở rộng

Toán tử mở rộng sử dụng vòng lặp for…of bên trong, vì vậy nó cũng có thể được sử dụng với cấu trúc Set.

let set = new Set(['red', 'green', 'blue']);
 
let arr = [...set];
 
console.log(arr); // Output: ['red', 'green', 'blue']

Các phép hợp, giao và hiệu của tập hợp

Sử dụng Set, bạn có thể dễ dàng thực hiện các phép hợp (Union), giao (Interest) và hiệu (Difference).

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
 
// Hợp
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
 
// Giao
let interest = new Set([...a].filter(x => b.has(x)));
// Set {2, 3}
 
// Hiệu
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

Thay đổi cấu trúc

Nếu bạn muốn thay đổi cấu trúc Set gốc trong quá trình lặp, hiện tại không có phương pháp trực tiếp, nhưng có hai phương pháp thay thế. Một là sử dụng cấu trúc Set gốc để tạo ra một cấu trúc mới, sau đó gán lại cho cấu trúc Set gốc; hai là sử dụng phương thức Array.from.

// Phương pháp A
let s1 = new Set([1, 2, 3]);
s1 = new Set([...set].map(val => val * 2));
 
console.log(set); // Output: 2, 4, 6
 
// Phương pháp B
let s2 = new Set([1, 2, 3]);
s2 = new Set(Array.from(set, val => val * 2));
 
console.log(s2);
// Output: 2, 4, 6