Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Lớp Set (Tập hợp) trong JavaScript - Triển khai custom & ES6

Set (Tập hợp) là cấu trúc dữ liệu lưu trữ các giá trị duy nhất (unique), không có key-value, chỉ có value. ES6 cung cấp Set chuẩn, nhưng có thể tự triển khai để hiểu bản chất.

1. Triển khai Custom MySet (ES6 Class) - Học tập

class MySet {
    constructor() {
        this.container = {}; // Dùng Object làm backend (key = value)
    }
    
    // Hiển thị
    display() {
        console.log(this.container);
    }
    
    // Kiểm tra tồn tại
    has(val) {
        return this.container.hasOwnProperty(val);
    }
    
    // Thêm
    add(val) {
        if (!this.has(val)) {
            this.container[val] = val; // Key = Value
            return true; // Đã thêm mới
        }
        return false; // Đã tồn tại
    }
    
    // Xóa
    delete(val) {
        if (this.has(val)) {
            delete this.container[val];
            return true;
        }
        return false;
    }
    
    // Xóa tất cả
    clear() {
        this.container = {};
    }
    
    // Duyệt
    forEach(callback) {
        for (let prop in this.container) {
            // prop là key (string), value = this.container[prop]
            // Vì key=value, nên callback(prop) hoặc callback(this.container[prop])
            callback(prop); // Truyền value (đã là string key)
        }
    }
    
    // Union (Hợp) - Static method
    static union(s1, s2) {
        if (!(s1 instanceof MySet) || !(s2 instanceof MySet)) {
            console.log("Not MySet instances");
            return null;
        }
        const newSet = new MySet();
        s1.forEach(elem => newSet.add(elem));
        s2.forEach(elem => newSet.add(elem));
        return newSet;
    }
    
    // Difference (Hiệu) - Static method: s1 - s2
    static difference(s1, s2) {
        if (!(s1 instanceof MySet) || !(s2 instanceof MySet)) {
            console.log("Not MySet instances");
            return null;
        }
        const newSet = new MySet();
        s1.forEach(elem => newSet.add(elem));
        s2.forEach(elem => newSet.delete(elem));
        return newSet;
    }
    
    // Intersection (Giao) - Bonus
    static intersection(s1, s2) {
        if (!(s1 instanceof MySet) || !(s2 instanceof MySet)) return null;
        const newSet = new MySet();
        s1.forEach(elem => { if (s2.has(elem)) newSet.add(elem); });
        return newSet;
    }
}

// Test MySet
const setA = new MySet();
setA.add(1); setA.add(2); setA.add(2); // Thêm trùng -> bỏ qua
setA.add(3);
setA.display(); // { '1': 1, '2': 2, '3': 3 }
console.log(setA.has(2)); // true
setA.delete(2);
setA.display(); // { '1': 1, '3': 3 }

2. ES6 Set (Chuẩn, Khuyên dùng Production)

// Tạo
const set1 = new Set();
const set2 = new Set([1, 2, 3, 3, 4]); // Tự động unique -> {1, 2, 3, 4}

// Thêm
set1.add(10).add(20).add(30); // Chainable

// Kiểm tra
console.log(set1.has(10)); // true
console.log(set1.has(99)); // false

// Xóa
set1.delete(20); // true
set1.delete(99); // false

// Kích thước
console.log(set1.size); // 2

// Duyệt
set1.forEach(val => console.log(val)); // 10, 30
for (const val of set1) console.log(val); // 10, 30

// Chuyển Array
const arr = [...set1]; // [10, 30]
const arr2 = Array.from(set1);

// Xóa tất cả
set1.clear();

3. Các phép toán Set (Union, Difference, Intersection) - ES6

ES6 Set KHÔNG có sẵn union/difference/intersection. Tự viết helper hoặc extends.

Union (Hợp): A ∪ B

function union(setA, setB) {
    if (!(setA instanceof Set) || !(setB instanceof Set)) return null;
    return new Set([...setA, ...setB]);
}
// Hoặc extends
Set.union = function(s1, s2) {
    if (!(s1 instanceof Set) || !(s2 instanceof Set)) return null;
    return new Set([...s1, ...s2]);
};

// Test
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
console.log(Set.union(a, b)); // Set {1, 2, 3, 4, 5, 6}

Difference (Hiệu): A - B

function difference(setA, setB) {
    if (!(setA instanceof Set) || !(setB instanceof Set)) return null;
    const result = new Set(setA);
    for (const val of setB) result.delete(val);
    return result;
}
// Hoặc extends
Set.difference = function(s1, s2) {
    if (!(s1 instanceof Set) || !(s2 instanceof Set)) return null;
    const result = new Set(s1);
    for (const val of s2) result.delete(val);
    return result;
};

// Test
console.log(Set.difference(a, b)); // Set {1, 2}

Intersection (Giao): A ∩ B

function intersection(setA, setB) {
    if (!(setA instanceof Set) || !(setB instanceof Set)) return null;
    return new Set([...setA].filter(x => setB.has(x)));
}
// Test
console.log(Set.intersection(a, b)); // Set {3, 4}

So sánh MySet (Custom) vs ES6 Set

Đặc điểmMySet (Object backend)ES6 Set
Value TypeChỉ String/Symbol (Object key)Bất kỳ (Object, Function, NaN, ...)
NaN handlingNaN key = "NaN" stringHỗ trợ NaN đúng chuẩn (NaN === NaN trong Set)
SizeObject.keys().length.size property
Iteration OrderInsertion order (ES2015+ Object)Insertion order (Chuẩn)
Perf Add/DeleteTốtTối ưu hơn
MethodsCustom (union, diff static)Core: add, delete, has, clear, forEach, size

Ứng dụng Set thực tế

  • Loại bỏ trùng lặp Array: [...new Set(arr)].
  • Kiểm tra tồn tại O(1): allowedValues.has(input).
  • Phép toán tập hợp: Tag system, Permission (Union/Intersection/Difference).
  • Unique ID tracking: Visited nodes in Graph/DFS/BFS.

Tóm lại: Set lưu giá trị unique. ES6 Set là chuẩn, mạnh, hỗ trợ mọi kiểu, NaN đúng. Custom MySet dùng Object backend (chỉ string key). ES6 Set thiếu union/diff/intersection -> tự viết helper hoặc extends prototype. Ứng dụng: unique array, O(1) lookup, set operations.