Trong lập trình JavaScript, Set và Array là hai cấu trúc dữ liệu phổ biến để lưu trữ tập hợp các phần tử. Tuy nhiên, mỗi loại có đặc điểm riêng và phù hợp với những tình huống khác nhau. Bài viết này sẽ phân tích chi tiết sự khác biệt giữa chúng, khi nào nên sử dụng từng loại, và cách chuyển đổi qua lại.
1. Những điểm khác biệt cơ bản
Tính duy nhất của phần tử:
Settự động loại bỏ các phần tử trùng lặp. Mỗi giá trị chỉ xuất hiện một lần trong tập hợp.Arraycho phép lưu trữ nhiều phần tử giống nhau mà không có hạn chế.
Thứ tự lưu trữ:
Setduy trì thứ tự theo thời điểm thêm vào.Arraysắp xếp phần tử theo chỉ mục (index), bắt đầu từ 0.
Hiệu suất tìm kiếm:
- Với
Set, phương thứchas()hoạt động với độ phức tạp O(1) - tức là thời gian không đổi bất kể kích thước tập hợp. - Với
Array, phương thứcincludes()có độ phức tạp O(n) - thời gian tăng tuyến tính theo số lượng phần tử.
Các thao tác cơ bản:
Setcung cấp:add()để thêm,delete()để xóa,clear()để xóa tất cả.Arraycung cấp:push()thêm vào cuối,pop()xóa từ cuối,splice()chèn/xóa tại vị trí bất kỳ.
Truy cập theo chỉ mục:
Setkhông hỗ trợ truy cập qua index nhưset[0].Arraycho phép truy cập trực tiếp qua chỉ mục nhưarr[0].
Khả năng lặp:
- Cả hai đều hỗ trợ
for...ofvàforEach. Arraycó thêm các phương thức xử lý mảng cao cấp nhưmap(),filter(),reduce().
2. Khi nào nên sử dụng Set
Trường hợp cần loại bỏ trùng lặp tự động:
Khi bạn cần thu thập dữ liệu mà không muốn các giá trị trùng nhau xuất hiện nhiều lần.
const danhSachMonAn = new Set(['pho', 'bun', 'pho']);
// Kết quả: Set(2) { 'pho', 'bun' }
Trường hợp cần kiểm tra nhanh sự tồn tại của phần tử:
Khi ứng dụng cần xác nhận một giá trị có trong tập hợp hay không thường xuyên.
const danhSachQuyen = new Set(['view', 'edit', 'delete']);
if (danhSachQuyen.has('edit')) {
// Thực hiện hành động chỉnh sửa
}
Trường hợp cần thao tác thêm/xóa với hiệu suất cao:
Các opertion add và delete trong Set có độ phức tạp O(1), phù hợp với các ứng dụng cần thay đổi dữ liệu liên tục.
3. Khi nào nên sử dụng Array
Trường hợp cần giữ lại các giá trị trùng lặp:
Khi dữ liệu cần lưu trữ tất cả các lần xuất hiện, không loại bỏ bất kỳ phần tử nào.
Trường hợp cần truy cập theo vị trí hoặc thao tác theo thứ tự:
Khi cần phân trang, sắp xếp, hoặc lấy một phần của tập hợp.
const mangSo = [5, 2, 8, 1, 9];
const sapXep = mangSo.sort((a, b) => a - b);
// Kết quả: [1, 2, 5, 8, 9]
Trường hợp cần xử lý dữ liệu bằng các hàm bậc cao:
Khi cần biến đổi, lọc, hoặc tổng hợp dữ liệu.
const mangChu = ['hello', 'world'];
const vietHoa = mangChu.map(chuoi => chuoi.toUpperCase());
// Kết quả: ['HELLO', 'WORLD']
4. Cách chuyển đổi giữa Set và Array
Từ Array sang Set:
Sử dụng constructor của Set và truyền vào một mảng. Kết quả sẽ tự động loại bỏ các phần tử trùng lặp.
const mangGoc = [10, 20, 20, 30];
const tapHop = new Set(mangGoc);
// Kết quả: Set(3) { 10, 20, 30 }
Từ Set sang Array:
Có hai cách phổ biến để chuyển đổi:
const tapHop = new Set([100, 200, 300]);
// Cách 1: Sử dụng Array.from()
const mangMoi = Array.from(tapHop);
// Kết quả: [100, 200, 300]
// Cách 2: Sử dụng toán tử spread
const mangKhac = [...tapHop];
// Kết quả: [100, 200, 300]
5. Ví dụ kết hợp sử dụng
Loại bỏ trùng lặp trong mảng:
const mangCanLoc = [1, 2, 2, 3, 3, 3];
const mangDuyNhat = [...new Set(mangCanLoc)];
// Kết quả: [1, 2, 3]
Thực hiện các phép toán tập hợp:
const tapA = new Set([1, 2, 3, 4]);
const tapB = new Set([3, 4, 5, 6]);
// Hợp nhất
const hopNhat = new Set([...tapA, ...tapB]);
// Kết quả: Set(6) { 1, 2, 3, 4, 5, 6 }
// Giao nhau
const giaoNhau = new Set([...tapA].filter(giaTri => tapB.has(giaTri)));
// Kết quả: Set(2) { 3, 4 }
// Hiệu (A - B)
const hieuAB = new Set([...tapA].filter(giaTri => !tapB.has(giaTri)));
// Kết quả: Set(2) { 1, 2 }
6. Lưu ý quan trọng
- Ưu tiên
Setkhi cần loại bỏ trùng lặp, tìm kiếm nhanh, hoặc thao tác thêm/xóa với tần suất cao. - Ưu tiên
Arraykhi cần giữ lại các giá trị trùng, truy cập qua index, hoặc sử dụng các phương thức xử lý dữ liệu bậc cao. - Để loại bỏ trùng lặp trong mảng:
[...new Set(mangGoc)] - Để chuyển Set thành mảng:
Array.from(tapHop)hoặc[...tapHop]