Seriya: JavaScript Basics Dars 34

Set

Set nima, nega array.includes() o'rniga Set kerak, V8 ichida hash table, SameValueZero, array'ni noyob qilish patterni va Set vs Array solishtiruvi.

34-dars

Set

1. Set nima — asosiy g'oya

Set — faqat noyob (unique) qiymatlarni saqlaydigan kolleksiya. Mapdan farqi: Map — key-value juftlik, Set — faqat qiymatlarning o'zi (key va value bir xil, aslida "key" tushunchasi yo'q).

const set = new Set();

set.add(1);
set.add(2);
set.add(2); // TAKRORLANGAN — E'TIBORGA OLINMAYDI!
set.add('2'); // BOSHQA qiymat (string, number emas) — qo'shiladi

console.log(set.size); // 3
console.log([...set]); // [1, 2, '2']

2. Nega oddiy array emas, Set kerak — muammoni ko'ramiz

// Array bilan noyoblikni ta'minlash — QIMMAT operatsiya
const arr = [1, 2, 3];
function addUnique(arr, val) {
  if (!arr.includes(val)) { // includes — O(n), HAR SAFAR BUTUN ARRAYNI TEKSHIRADI!
    arr.push(val);
  }
}

arr.includes() — chiziqli qidiruv (O(n)): har bir qo'shishda, butun array bo'ylab yurish kerak. Agar minglab element bo'lsa — bu juda sekin bo'lib qoladi (minglab qo'shish = millionlab taqqoslash).

// Set bilan — TEZ, chunki hash table asosida
const set = new Set([1, 2, 3]);
set.add(4); // O(1) — hash orqali, DARHOL tekshiriladi, qidiruv kerak emas

Set ichkarida — xuddi Map kabi hash table asosida ishlaydi, shuning uchun has(), add() — O(1) (o'rtacha holatda), array'ning includes() kabi O(n) emas.


3. V8 ichida Set qanday saqlanadi

Set ICHKI TUZILISHI:

┌─────────────────────────────────┐
│ HashTable                        │
│  bucket[hash(1)] → entry: 1       │
│  bucket[hash(2)] → entry: 2       │
│  bucket[hash('2')] → entry: '2'   │
├─────────────────────────────────┤
│ Insertion Order Linked List       │  ← Map'dagi kabi, TARTIB SAQLANADI
│  1 → 2 → '2'                      │
└─────────────────────────────────┘

Bu — Mapning deyarli aynan bir xil ichki mexanizmi, faqat har bir "entry"da faqat bitta qiymat bor (key=value, aslida ikkalasi bir xil narsa). Ba'zi V8 versiyalarida Map va Set uchun deyarli bir xil ichki OrderedHashTable C++ klass orqali implement qilingan.


4. Noyoblik qanday aniqlanadi — SameValueZero algoritmi

const set = new Set();

set.add(NaN);
set.add(NaN);
console.log(set.size); // 1! — NaN faqat BIR MARTA qo'shildi

console.log(NaN === NaN); // false (odatiy === solishtirish)

Nozik nuqta: Set — === (Strict Equality)dan emas, balki SameValueZero algoritmidan foydalanadi. Bu ikkalasi deyarli bir xil, faqat bitta farq: SameValueZero'da NaN === NaN — true hisoblanadi (odatiy ===da esa false). Bu — Map'da ham xuddi shunday ishlaydi.

const set2 = new Set();
set2.add(+0);
set2.add(-0);
console.log(set2.size); // 1 — +0 va -0 BIR XIL hisoblanadi (SameValueZero'da)

5. Obyektlar bilan Set — pointer orqali solishtirish

const set = new Set();
set.add({ id: 1 });
set.add({ id: 1 }); // BOSHQA obyekt (boshqa pointer) — QO'SHILADI!

console.log(set.size); // 2! — mazmuni bir xil bo'lsa ham, IKKI XIL manzil

Set — obyektlarni mazmuni bo'yicha emas, pointer (manzil) bo'yicha solishtiradi — chunki Set ichkarida ham ===/SameValueZero ishlatadi, va biz bilamizki, {} === {} — har doim false.


6. Amaliy holat 1 — Array'ni noyob qilish (eng ko'p ishlatiladigan pattern)

const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4]

Bu — bitta qatorda massivni noyob qilishning eng tez, eng qisqa usuli. Ichkarida: new Set(arr) — array elementlarini hash tablega qo'shadi (takrorlar avtomatik tashlanadi), keyin spread (...) orqali qaytadan array'ga aylantiriladi.


7. Amaliy holat 2 — tez has() tekshiruvi (performance uchun)

// ❌ SEKIN — har safar O(n) qidiruv
const blockedUsers = ['user1', 'user2', 'user3', /* ...minglab element */];
function isBlocked(user) {
  return blockedUsers.includes(user); // O(n) — HAR SAFAR!
}

// ✅ TEZ — O(1) qidiruv
const blockedUsersSet = new Set(['user1', 'user2', 'user3', /* ...minglab element */]);
function isBlocked(user) {
  return blockedUsersSet.has(user); // O(1) — hash orqali darhol
}

Qoida: agar sizga tez-tez "shu qiymat bormi?" tekshiruvi kerak bo'lsa (masalan "blocklist", "allowlist", "ko'rilgan ID'lar"), array o'rniga har doim Set ishlating.


8. Set metodlari — to'liq

const set = new Set([1, 2, 3]);

set.has(2);      // true
set.add(4);       // Set { 1, 2, 3, 4 }
set.delete(1);    // true — o'chirildi
set.size;         // 3

for (const val of set) {
  console.log(val); // 2, 3, 4 — qo'shilish tartibida
}

set.forEach(val => console.log(val));

[...set]; // [2, 3, 4] — array'ga aylantirish

set.clear(); // barchasini tozalash

Qiziq nozik nuqta: Set'da keys() va values() — bir xil natija qaytaradi (chunki key va value farqi yo'q), bu — API'ni Map bilan izchil (consistent) qilish uchun ataylab shunday qilingan:

console.log([...set.keys()]);    // [2, 3, 4]
console.log([...set.values()]);  // [2, 3, 4] — XUDDI SHU
console.log([...set.entries()]); // [[2,2], [3,3], [4,4]] — [value, value] juftligi

9. Set vs Array — to'liq solishtirish jadvali

Array Set
Takrorlanuvchi qiymat Ruxsat etiladi Avtomatik rad etiladi
includes/has tezligi O(n) — chiziqli qidiruv O(1) — hash orqali (o'rtacha)
Index orqali kirish (arr[0]) ✅ Bor ❌ Yo'q — faqat iteratsiya orqali
Tartib Index bo'yicha, kafolatlangan Qo'shilish tartibida, kafolatlangan
Element turi Har qanday, PACKED/HOLEY elements kind Har qanday, lekin hash table asosida

10. WeakSet bilan chalkashtirmaslik uchun eslatma

Set — obyektlarni kuchli (strong) referens bilan saqlaydi, ya'ni agar siz obyektni Set'ga qo'shsangiz, u boshqa hech qayerda ishlatilmasa ham, garbage collector uni yig'ib olmaydi (chunki Set unga hali ham ishora qilib turibdi):

let obj = { data: 'katta' };
const set = new Set([obj]);
obj = null; // asosiy referensni o'chirdik

// Lekin obyekt HALI HAM XOTIRADA — chunki 'set' unga ishora qilib turibdi!
console.log([...set][0]); // { data: 'katta' } — hali ham mavjud

Bu — memory leak manbai bo'lishi mumkin. Aynan shu muammoni WeakSet hal qiladi.


Xulosa

Set — Mapning ichki bir xil hash table + insertion-order mexanizmiga asoslangan, lekin faqat noyob qiymatlarni saqlaydigan kolleksiya; u SameValueZero algoritmi orqali takrorlarni avtomatik chiqarib tashlaydi, has()/add() operatsiyalari O(1) tezlikda ishlaydi (array'ning includes()dagi O(n)dan farqli), va u ham, Map kabi, obyekt qiymatlarini pointer orqali (mazmun emas) solishtiradi.