IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur44/65-dars22 daqiqa
Mundarija (37)

JavaScript Map: istalgan turdagi kalitli lug'at

Qisqacha: Map — kalit-qiymat juftliklarini saqlaydigan maxsus to'plam. Oddiy obyektdan farqi: kalit istalgan turda bo'ladi (son, boolean, hatto obyekt) va satrga aylantirilmaydi, juftliklar qo'shilgan tartibda turadi, soni esa size da tayyor. Ishlatish: const narxlar = new Map(); narxlar.set("osh", 35000); narxlar.get("osh").

Bu darsda

  • Map yaratib, set, get, has, delete, clear va size bilan ishlay olasiz.
  • Son, boolean va obyektni kalit qila olasiz va ularning tuzog'ini bilasiz.
  • Map bo'ylab for...of va forEach bilan yura olasiz, uni massiv va obyektga aylantira olasiz.
  • Qachon oddiy obyekt, qachon Map kerakligini tanlay olasiz.
  • vazifalar ga teglar hisobini qo'shasiz.

Oldin bilishingiz kerak: Obyekt: kalit-qiymat juftliklari, for...of va for...in, reduce, Guruhlash: Object.groupBy va Map.groupBy, Prototype pollution, Closure amaliyotda.

1. Nega bu kerak?

08-qismdan beri lug'at kerak bo'lsa, oddiy obyekt olardik: { osh: 35000, manti: 30000 }. Bu ko'p hollarda yaxshi ishlaydi. Lekin shu qismda obyekt bir necha marta "qoqildi":

  • Guruhlash darsida stol raqami 3 kalit bo'lganda "3" satriga aylandi. Keyin uni Number bilan qaytarishga to'g'ri keldi.
  • O'sha darsda "constructor" degan teg butun dasturni to'xtatdi: obyekt bu kalitni prototipdan meros olgan edi.
  • Prototype pollution darsida __proto__ kaliti obyektning prototipini almashtirib yubordi.
  • Closure amaliyotda darsidagi keshlangan funksiya 3 va "3" ni bitta kalit deb o'yladi.

Hammasining ildizi bitta. Obyekt aslida lug'at uchun yaratilmagan: u dasturdagi narsani (taom, foydalanuvchi, vazifa) tasvirlash uchun. Kalitlari doim satr va uning prototipi bor.

Shu muammolar uchun JavaScript'da maxsus to'plam bor — Map. Hayotdan o'xshatish: kiyim saqlash xonasi (garderob). Siz paltoni topshirasiz, raqamli jeton olasiz. Jeton raqam bo'ladimi, rangli plastinkami — farqi yo'q. Xodim uni hech narsaga "aylantirmaydi", paltoni aynan shu jetonga beradi. Xonada boshqa birovning eski paltolari ham "meros" bo'lib osilib turmaydi.

2. Map yaratish va asosiy metodlar

2.1 Birinchi Map

Map — kalit-qiymat juftliklari to'plami, unda kalit istalgan turdagi qiymat bo'lishi mumkin. Map — JavaScript'ga tayyor qo'shib qo'yilgan klass. Shuning uchun u o'zingiz yozgan klasslar kabi new bilan yaratiladi:

js
const narxlar = new Map();

narxlar.set("osh", 35000);
narxlar.set("manti", 30000);

console.log(narxlar.get("osh")); // 35000
console.log(narxlar.size); // 2
console.log(narxlar);

Konsolda:

text
35000
2
Map(2) { 'osh' => 35000, 'manti' => 30000 }

Node Map ni o'ziga xos ko'rinishda chiqaradi: qavs oldida Map(2) — juftliklar soni, ichida esa kalit => qiymat strelkalari. Brauzer konsoli Map(2) {'osh' => 35000, 'manti' => 30000} deb ko'rsatadi — ma'nosi bir xil.

Nuqta bilan yozish (narxlar.osh) bu yerda ishlamaydi. Map da juftlik faqat metodlar orqali o'qiladi va yoziladi. Nega shunday — «Ko'p uchraydigan xatolar» bo'limida ko'ramiz.

2.2 Oltita asosiy a'zo

Jadvalda m — biror Map, k — kalit, q — qiymat:

Yozuv Nima qiladi Qaytaradi
m.set(k, q) juftlik qo'shadi yoki qiymatni yangilaydi Map ning o'zi
m.get(k) kalit bo'yicha qiymatni oladi qiymat yoki undefined
m.has(k) kalit bormi? true / false
m.delete(k) juftlikni o'chiradi o'chdimi: true / false
m.clear() hammasini o'chiradi undefined
m.size juftliklar soni (xususiyat, metod emas) son

Hammasini bitta misolda ko'ramiz. Otabek stollar bandligini yozib boradi:

js
const stollar = new Map();

stollar.set(1, "Dilshod aka").set(3, "Malika").set(5, "Bobur");
console.log(stollar.size); // 3

console.log(stollar.has(3)); // true
console.log(stollar.delete(3)); // true
console.log(stollar.delete(3)); // false
console.log(stollar.get(3)); // undefined

stollar.set(1, "Nodira");
console.log(stollar.get(1)); // Nodira

stollar.clear();
console.log(stollar.size); // 0

Birinchi qatorga qarang: set uch marta zanjir bo'lib chaqirildi. Bu mumkin, chunki set Map ning o'zini qaytaradi. Ikkinchi delete(3) esa false berdi — o'chiradigan narsa qolmagan edi. set(1, ...) mavjud kalitga yangi qiymat yozdi, yangi juftlik qo'shmadi.

size — oddiy xususiyat, qavssiz yoziladi. Obyektda kalitlar sonini bilish uchun Object.keys(obj).length yozardik. Bu har safar vaqtinchalik massiv yasaydi. Map esa sonini doim tayyor saqlaydi.

2.3 Boshlang'ich juftliklar bilan yaratish

new Map() ga juftliklar massivini bersangiz, Map darhol to'ladi. Har juftlik — ikki elementli massiv: [kalit, qiymat].

js
const menyu = new Map([
  ["osh", 35000],
  ["lag'mon", 28000],
  ["ko'k choy", 5000],
]);

console.log(menyu.get("lag'mon")); // 28000

Shakl tanish: Object.entries aynan shunday juftliklar massivini qaytaradi. Demak obyektdan Map yasash — bitta qator: new Map(Object.entries(obj)).

2.4 get dagi undefined — ikki ma'no

js
const qoldiq = new Map([["osh", undefined]]);

console.log(qoldiq.get("osh")); // undefined
console.log(qoldiq.get("manti")); // undefined
console.log(qoldiq.has("osh")); // true
console.log(qoldiq.has("manti")); // false

get ikkala holatda undefined qaytardi. Lekin birinchisida kalit bor, qiymati undefined. Ikkinchisida kalitning o'zi yo'q. Farqni faqat has ko'rsatadi. Qoida: "bormi?" savoli uchun doim has ni ishlating.

Tekshirib ko'ring: new Map().set("a", 1).set("b", 2).size nimaga teng?

Javob

2. new Map() bo'sh to'plam yaratadi. Birinchi set uni qaytaradi, ikkinchi set shu to'plamga yana bitta juftlik qo'shadi va yana uni qaytaradi. Oxirida .size — ikki juftlik.

3. Kalit — istalgan tur

3.1 Son satr emas

Obyektda obj[3] va obj["3"] — bitta kalit. Map da — ikki xil:

js
const m = new Map();
m.set(3, "son");
m.set("3", "satr");
m.set(true, "boolean");

console.log(m.size); // 3
console.log(m.get(3)); // son
console.log(m.get("3")); // satr
console.log(m.get(true)); // boolean

Kalit qanday bergan bo'lsangiz, shunday saqlanadi. Bu foydali, lekin ehtiyot bo'lish kerak. Forma maydonidan kelgan qiymat doim satr (Forma qiymatlarini o'qish). Stol raqami son bilan saqlangan bo'lsa, stollar.get(input.value) undefined qaytaradi. Kalitni avval kerakli turga keltiring: stollar.get(Number(input.value)).

3.2 Kalitlar qanday solishtiriladi

Map ikki kalitni deyarli === kabi solishtiradi. Bitta farqi bor: NaN o'ziga teng hisoblanadi. Bu qoidaning nomi — SameValueZero, uni Tenglik operatorlari darsida includes orqali ko'rgan edingiz. 0 va -0 ham bitta kalit.

js
const m = new Map([[NaN, "son emas"]]);
m.set(0, "nol");

console.log(m.get(NaN)); // son emas
console.log(m.get(-0)); // nol

3.3 Obyekt kalit: havola bo'yicha

Endi Map ning eng kuchli imkoniyati. Kalit obyekt bo'lishi mumkin. Guruhlash darsida taksi safarlarini haydovchi obyekti bo'yicha guruhlagan edik. Endi bu to'g'ridan-to'g'ri:

js
const shuhrat = { ism: "Shuhrat" };
const olim = { ism: "Olim" };

const safarlar = new Map();
safarlar.set(shuhrat, 2);
safarlar.set(olim, 5);

console.log(safarlar.get(shuhrat)); // 2
console.log(safarlar.size); // 2

Oddiy obyektga shunday yozsangiz, ikkala haydovchi ham "[object Object]" satriga aylanib, bitta kalitga tushardi. Map esa obyektning o'zini kalit qiladi.

Lekin bu yerda bitta muhim tuzoq bor. Obyektlar ichidagi ma'lumot bo'yicha emas, havola bo'yicha solishtiriladi (Havola semantikasi):

js
const safarlar = new Map();
safarlar.set({ ism: "Shuhrat" }, 2);

console.log(safarlar.get({ ism: "Shuhrat" })); // undefined

Ikki { ism: "Shuhrat" } — tashqi ko'rinishi bir xil, lekin xotirada ikki alohida obyekt. get boshqa obyekt bilan qidirdi va hech narsa topmadi. Obyekt kalitdan foydalanish uchun aynan o'sha obyektning havolasi kerak — odatda u o'zgaruvchida yoki boshqa ro'yxatda saqlanadi.

Tekshirib ko'ring: const m = new Map([[[1], "a"]]); — m.get([1]) nima qaytaradi?

Javob

undefined. Massiv ham obyekt. Kalit sifatida berilgan [1] va get ichidagi [1] — ikki xil massiv, havolalari har xil. Massivni kalit qilish kerak bo'lsa, uni avval o'zgaruvchiga oling va o'sha o'zgaruvchi bilan ishlang.

4. Map bo'ylab yurish

4.1 for...of va juftliklar

Map ni for...of bilan aylanish mumkin. Har qadamda [kalit, qiymat] juftligi keladi — uni darhol massiv destructuring bilan ochamiz:

js
const menyu = new Map([
  ["osh", 35000],
  ["manti", 30000],
  ["ko'k choy", 5000],
]);

for (const [taom, narx] of menyu) {
  console.log(`${taom}: ${narx} so'm`);
}

Konsolda:

text
osh: 35000 so'm
manti: 30000 so'm
ko'k choy: 5000 so'm

Juftliklar qo'shilgan tartibda keldi. Bu Map ning kafolati. Obyektda esa butun songa o'xshash kalitlar doim oldinga chiqib ketardi (Obyekt bo'ylab yurish):

js
const obyekt = { b: 1, 2: 1, a: 1, 1: 1 };
const xarita = new Map([["b", 1], [2, 1], ["a", 1], [1, 1]]);

console.log(Object.keys(obyekt)); // [ '1', '2', 'b', 'a' ]
console.log([...xarita.keys()]); // [ 'b', 2, 'a', 1 ]

Mavjud kalitga qayta set qilish tartibni o'zgartirmaydi. O'chirib, keyin qo'shsangiz — juftlik oxiriga o'tadi.

for...of nega massivda ham, satrda ham, Map da ham ishlaydi? Ularning hammasi bitta umumiy kelishuvga amal qiladi. Uni Iteratsiya protokoli darsida ochamiz — hozir bilish shart emas.

4.2 keys, values, entries

Faqat kalitlar yoki faqat qiymatlar kerak bo'lsa — uchta metod bor:

js
const menyu = new Map([
  ["osh", 35000],
  ["manti", 30000],
]);

console.log(menyu.keys());
console.log([...menyu.keys()]);
console.log([...menyu.values()]);
console.log([...menyu.entries()]);

Konsolda:

text
[Map Iterator] { 'osh', 'manti' }
[ 'osh', 'manti' ]
[ 35000, 30000 ]
[ [ 'osh', 35000 ], [ 'manti', 30000 ] ]

Birinchi qatorga e'tibor bering: keys() massiv emas, [Map Iterator] qaytardi. Iterator — elementlarni bittadan beradigan maxsus obyekt; unda map yoki filter yo'q. Massiv kerak bo'lsa — spread yoki Array.from bilan aylantiring. [...menyu] ham entries() bilan bir xil natija beradi.

4.3 forEach — tartibi teskari imzo

Map da forEach metodi ham bor. Lekin callback imzosi massivdagidan farq qiladi:

js
const menyu = new Map([["osh", 35000]]);

menyu.forEach((narx, taom, hammasi) => {
  console.log(taom, narx, hammasi.size);
});

Konsolda:

text
osh 35000 1

Birinchi parametrga qiymat, ikkinchisiga kalit tushadi. Nega teskari? Massivdagi forEach bilan bir xil bo'lishi uchun. U yerda ham birinchisi — qiymat (element), ikkinchisi — uning "manzili" (indeks). Map da manzil o'rnida kalit turadi. Kalitni birinchi o'ringa kutib, (taom, narx) deb yozish — tez-tez uchraydigan xato. Shuning uchun ko'p dasturchilar for...of + destructuring'ni afzal ko'radi: unda tartib ko'zga ko'rinib turadi.

4.4 Map bilan sanash — qadamma-qadam

Map ning eng ko'p uchraydigan ishi — sanash. Kunlik buyurtmalardagi har taom necha marta so'ralgan? Keyingi tugmasini bosib, sanoq ichida nima bo'layotganini kuzating:

To'rtinchi qator — sanashning butun siri: "bor qiymatni ol, yo'q bo'lsa 0 deb ol, bittaga oshirib yoz". ?? operatori undefined ni 0 ga almashtiradi.

O'zingiz to'ldiring. ["a", "b", "a", "a"] ni shu usulda sanasak, sanoq.get("a") — [:3], sanoq.size esa — [:2].

5. Map ↔ obyekt va JSON

5.1 Ikki tomonga aylantirish

js
const menyu = { osh: 35000, manti: 30000 };

const xarita = new Map(Object.entries(menyu));
xarita.set("ko'k choy", 5000);

const yana = Object.fromEntries(xarita);
console.log(yana);

Konsolda:

text
{ osh: 35000, manti: 30000, "ko'k choy": 5000 }

Object.fromEntries — Object.entries ning teskarisi (Object.* metodlari). U har qanday juftliklar to'plamini, shu jumladan Map ni, obyektga aylantiradi. Faqat esda tuting: obyektga o'tganda son va obyekt kalitlar yana satrga aylanadi.

5.2 JSON Map ni bilmaydi

JSON chuqur darsidagi jadvalni eslang:

js
const menyu = new Map([["osh", 35000]]);

console.log(JSON.stringify(menyu)); // {}
console.log(JSON.stringify([...menyu])); // [["osh",35000]]
const obyekt = Object.fromEntries(menyu);
console.log(JSON.stringify(obyekt)); // {"osh":35000}

JSON.stringify Map ni bo'sh obyekt {} qilib yozdi — xatosiz, ma'lumot jimgina yo'qoldi. Saqlash yoki serverga yuborishdan oldin Map ni massivga yoki obyektga aylantiring. Kalitlar oddiy satr bo'lsa — obyekt qulay. Son yoki boolean kalitlar saqlanishi kerak bo'lsa — juftliklar massivi: uni o'qigach new Map(JSON.parse(matn)) bilan qayta tiklaysiz.

6. Hujumchi nigohi: kalitlar tashqaridan kelganda

Prototype pollution darsida hujumchi __proto__ kalitini yuborib, obyektning prototipini o'zgartirgan edi. Kalitlar foydalanuvchidan keladigan joylar xavfli: teglar, so'z sanog'i, URL parametrlari, forma maydonlari nomlari.

Oddiy obyekt va Map ni bir xil hujumli kalit bilan solishtiramiz:

js
const obyekt = {};
obyekt["__proto__"] = { admin: true };
console.log(obyekt.admin); // true
console.log(Object.keys(obyekt)); // []

const xarita = new Map();
xarita.set("__proto__", { admin: true });
console.log(xarita.size); // 1
console.log(xarita.get("admin")); // undefined

Obyektda __proto__ oddiy kalit bo'lib yozilmadi. U prototipni almashtirdi: obyekt.admin endi true, Object.keys esa hech narsa ko'rmaydi. Map da esa "__proto__" — shunchaki bitta satr kalit, boshqalardan farqi yo'q.

Meros kalitlar bilan ham shunday:

js
const sanoq = {};
console.log(sanoq["constructor"]); // [Function: Object]

const xarita = new Map();
console.log(xarita.get("constructor")); // undefined

Map da prototipdan meros kelgan juftliklar yo'q — faqat siz set qilgan narsa bor. Object.create va prototipal meros darsidagi Object.create(null) ham bu muammoni hal qiladi. Lekin Map qo'shimcha ravishda istalgan turdagi kalit, size va kafolatlangan tartibni beradi. Qoida: kalitlari tashqaridan keladigan lug'at — Map.

7. Map.groupBy — endi to'liq tushunarli

Guruhlash darsida Map.groupBy ni ko'rgan edik, lekin Map hali o'tilmagan edi. Endi uning natijasini to'liq o'qiy olamiz:

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Ko'k choy", narx: 5000 },
  { nom: "Lag'mon", narx: 28000 },
];

const guruhlar = Map.groupBy(menyu, (t) => t.narx > 20000);

for (const [qimmatmi, taomlar] of guruhlar) {
  console.log(qimmatmi, taomlar.map((t) => t.nom));
}

Konsolda:

text
true [ 'Osh', "Lag'mon" ]
false [ "Ko'k choy" ]

Natija — oddiy Map. Kalitlar haqiqiy true va false, har guruh — massiv. O'sha darsning 3-mashqida stol raqamini Number bilan qaytargan edik. Bugungi 3-mashqda shu masalani Map.groupBy bilan yechamiz — Number kerak bo'lmaydi.

8. Yangi imkoniyat: getOrInsert

Sanash qolipida har safar ikki qadam yozamiz: get va set. Shu ish uchun Map ga ikki yangi metod qo'shildi:

  • m.getOrInsert(kalit, qiymat) — kalit bo'lsa, qiymatini qaytaradi. Bo'lmasa, berilgan qiymatni yozadi va uni qaytaradi.
  • m.getOrInsertComputed(kalit, callback) — xuddi shunday, lekin yangi qiymatni faqat kerak bo'lganda callback hisoblaydi. Qiymatni yasash qimmat bo'lsa (masalan, katta massiv) — shunisi.

Guruhlash bilan ko'rinishi:

js
const guruhlar = new Map();
for (const t of menyu) {
  guruhlar.getOrInsert(t.tur, []).push(t);
}

Bu metodlar juda yangi. Baseline ma'lumotiga ko'ra ular Chrome va Edge 145, Firefox 144, Safari 26.2 da bor. 2026-yil 14-fevraldan ular Baseline'ning "yangi" (newly available) darajasida. Ya'ni hamma asosiy brauzerning eng so'nggi versiyasida bor, lekin yangilanmagan telefonlarda hali yo'q. Node 24 da esa umuman yo'q — sinab ko'rdik:

text
TypeError: m.getOrInsert is not a function

Tarjimasi: "m.getOrInsert — funksiya emas". Metod yo'q, demak m.getOrInsert — undefined, uni chaqirib bo'lmaydi. Shuning uchun bu kursda hozircha eski, har joyda ishlaydigan shaklni yozamiz:

js
const menyu = [
  { nom: "Osh", tur: "taom" },
  { nom: "Choy", tur: "ichimlik" },
  { nom: "Manti", tur: "taom" },
];

const guruhlar = new Map();
for (const t of menyu) {
  if (!guruhlar.has(t.tur)) {
    guruhlar.set(t.tur, []);
  }
  guruhlar.get(t.tur).push(t.nom);
}

console.log(guruhlar);

Konsolda:

text
Map(2) { 'taom' => [ 'Osh', 'Manti' ], 'ichimlik' => [ 'Choy' ] }

Real loyihada kodni qaysi muhitda ishlashiga qarab tanlaysiz: zamonaviy brauzerlar uchun getOrInsert, Node 24 va eski telefonlar uchun — has + set.

9. Obyekt yoki Map?

Ikkalasi ham lug'at bo'la oladi. Tanlash uchun sxemadagi savollarga yuqoridan pastga javob bering:

flowchart TD
  A{"Kalitlar oldindan ma'lummi?<br/>(nom, narx, id)"}
  A -- "ha, bu narsa tavsifi" --> O["Oddiy obyekt"]
  A -- "yo'q, ma'lumot bilan keladi" --> B{"Kalit satrmi?"}
  B -- "son, boolean, obyekt" --> M["Map"]
  B -- "satr" --> C{"Tashqaridan keladimi<br/>yoki tez-tez o'chiriladimi?"}
  C -- "ha" --> M
  C -- "yo'q" --> D{"JSON'ga ko'p<br/>yoziladimi?"}
  D -- "ha" --> O
  D -- "yo'q" --> M

Qisqasi:

  • Obyekt — bitta narsaning tavsifi: { id, matn, bajarildi }. Kalitlar kod yozilganda ma'lum, nuqta bilan o'qiladi, JSON'ga to'g'ridan-to'g'ri yoziladi.
  • Map — to'plam: kalitlar dastur ishlaganda paydo bo'ladi va yo'qoladi. Sanoqlar, keshlar, "id → obyekt" jadvallari, teglar.

Tezlik haqida bir gap. V8 (Chrome va Node'da JavaScript'ni bajaradigan dastur — dvigatel) Map ni tez-tez qo'shish va o'chirish uchun moslagan. Obyekt esa shakli o'zgarmaydigan "tavsif" sifatida tez. Kichik ma'lumotda farq sezilmaydi — avval ma'no bo'yicha tanlang.

Tekshirib ko'ring: Telegram bot har foydalanuvchining xabarlar sonini sanaydi. Kalit — foydalanuvchi id raqami (son). Obyektmi yoki Map?

Javob

Map. Kalitlar oldindan ma'lum emas (yangi foydalanuvchilar keladi), kalit — son, uni satrga aylantirmaslik qulay. Bundan tashqari foydalanuvchi soni size da tayyor.

10. Ko'p uchraydigan xatolar

10.1 Kvadrat qavs bilan yozish

js
const narxlar = new Map();
narxlar["osh"] = 35000;

console.log(narxlar.size); // 0
console.log(narxlar.get("osh")); // undefined
console.log(narxlar); // Map(0) { osh: 35000 }

Xato xabari yo'q, lekin juftlik qo'shilmadi. Map ham obyekt, shuning uchun narxlar["osh"] = ... unga oddiy xususiyat yozdi. Node buni Map(0) { osh: 35000 } deb ko'rsatdi: 0 — juftliklar soni, osh: 35000 — strelkasiz, ya'ni oddiy xususiyat. Tuzatish: faqat set va get.

10.2 Obyekt kalitni yangi obyekt bilan qidirish

«Obyekt kalit: havola bo'yicha» bo'limidagi get({ ism: "Shuhrat" }) — undefined. Tuzatish: kalitni o'zgaruvchida saqlang yoki kalit sifatida id kabi primitivni ishlating.

10.3 Son kalitni satr bilan o'qish

stollar.set(3, ...) keyin stollar.get("3") — undefined. Formadan, URL'dan, dataset dan keladigan qiymatlar doim satr. Tuzatish: get(Number(qiymat)) yoki kalitni boshidan satr qilib saqlang — muhimi, bir xil tur.

10.4 forEach da parametrlar tartibi

map.forEach((kalit, qiymat) => ...) — nomlar teskari: birinchi parametrga qiymat tushadi. Tuzatish: (qiymat, kalit) yoki for (const [kalit, qiymat] of map).

10.5 Map ni JSON'ga to'g'ridan-to'g'ri yozish

JSON.stringify(map) — "{}". Tuzatish: [...map] yoki Object.fromEntries(map).

11. Mashqlar

1-mashq (oson): Stollar bandligi

Bo'sh Map yarating. Unga uch stolni yozing: 2-stol — "Dilshod aka", 4-stol — "Nodira", 7-stol — "Bobur" (stol raqami — son). So'ng: 4-stol bandmi (has), 2-stolda kim (get), 7-stol bo'shadi (delete), nechta stol band (size) — har birini konsolga chiqaring.

Yechim
js
const band = new Map();
band.set(2, "Dilshod aka").set(4, "Nodira").set(7, "Bobur");

console.log(band.has(4)); // true
console.log(band.get(2)); // Dilshod aka
band.delete(7);
console.log(band.size); // 2

set Map ni qaytargani uchun uchalasini zanjir qildik. Kalitlar — son. band.has("4") deb yozsangiz, false chiqardi.

2-mashq (o'rta): Eng ko'p so'ralgan taom

Kunlik buyurtmalar ro'yxati berilgan. Map bilan har taomni sanang, so'ng eng ko'p so'ralganini toping. Natija: osh — 3 marta.

Ishora: sanash — «Map bilan sanash» bo'limidagi qolip. Eng kattasini topish uchun [...sanoq] ni reduce yoki toSorted bilan ishlang.

js
const buyurtmalar = ["osh", "manti", "osh", "choy", "manti", "osh"];
Yechim
js
const buyurtmalar = ["osh", "manti", "osh", "choy", "manti", "osh"];

const sanoq = new Map();
for (const taom of buyurtmalar) {
  sanoq.set(taom, (sanoq.get(taom) ?? 0) + 1);
}

const [eng, soni] = [...sanoq].reduce(
  (a, b) => (b[1] > a[1] ? b : a),
);
console.log(`${eng} — ${soni} marta`);

Konsolda:

text
osh — 3 marta

[...sanoq] — [["osh", 3], ["manti", 2], ["choy", 1]]. reduce boshlang'ich qiymatsiz chaqirildi: akkumulyator — birinchi juftlik. Har qadamda kattarog'i qoladi. Natija juftligini destructuring bilan ochdik.

3-mashq (qiyin): Eng ko'p tushum bergan stol — Map.groupBy bilan

Guruhlash darsidagi masala. Buyurtmalarni stol bo'yicha guruhlang, har stolning jami summasini hisoblang va { stol, summa } massivini summa kamayishi tartibida chiqaring. Bu safar Map.groupBy ishlating — stol raqami son bo'lib qolsin, Number kerak bo'lmasin.

js
const buyurtmalar = [
  { stol: 3, summa: 75000 },
  { stol: 1, summa: 40000 },
  { stol: 3, summa: 33000 },
  { stol: 7, summa: 120000 },
  { stol: 1, summa: 28000 },
];
Yechim
js
const buyurtmalar = [
  { stol: 3, summa: 75000 },
  { stol: 1, summa: 40000 },
  { stol: 3, summa: 33000 },
  { stol: 7, summa: 120000 },
  { stol: 1, summa: 28000 },
];

const stollar = Map.groupBy(buyurtmalar, (b) => b.stol);

const hisobot = [...stollar]
  .map(([stol, royxat]) => ({
    stol,
    summa: royxat.reduce((jami, b) => jami + b.summa, 0),
  }))
  .toSorted((a, b) => b.summa - a.summa);

console.log(hisobot);
console.log(stollar.has(3), stollar.has("3"));

Konsolda:

text
[
  { stol: 7, summa: 120000 },
  { stol: 3, summa: 108000 },
  { stol: 1, summa: 68000 }
]
true false

Ikki farq bor. Object.entries(stollar) o'rniga [...stollar] — Map dan juftliklar massivi. stol: Number(stol) o'rniga shunchaki stol — kalit son bo'lib qolgan. Oxirgi qator buni isbotlaydi: has(3) — true, has("3") — false.

4-mashq: Vazifalar qadami — teglar hisobi

vazifalar ga yangi imkoniyat qo'shamiz: teglar. Teg — vazifa matnidagi # bilan boshlanadigan so'z: "Sabzi olish #bozor #uy". Matnni maxsus ajratmaymiz — teglar shunchaki matn ichida yoziladi. Bu va keyingi ikki darsda ular bilan ishlaymiz.

Bugungi talab: hisob ostida yangi qator — Teglar: #bozor — 2, #uy — 1. Teg yo'q bo'lsa — qator yashirin.

  1. Yangi branch: git switch -c feature/teglar. Bu refaktoring emas — xulq o'zgaradi, shuning uchun prefiks feature/.
  2. HTML'da #hisob dan keyin: <p id="teglar" hidden></p>. JS'da "2. Sahifa elementlari" ga teglarQatori.
  3. Ikki yangi kod:
    • teglariniOl(matn) — matndagi teglar massivi. Oddiy toza funksiya, VazifalarRoyxati klassidan oldin. Ishora: split(" ") va filter — so'z # bilan boshlansin va undan uzunroq bo'lsin (Kesish, bo'lish va to'ldirish).
    • tegHisobi() — VazifalarRoyxati ning yangi metodi: Map, teg → nechta vazifada uchradi. Ishora: avval flatMap bilan #vazifalar dagi hamma teglarni bitta massivga yig'ing, so'ng reduce — akkumulyator new Map() (set Map ni qaytarishini eslang).
  4. "3. Hisoblovchi funksiyalar" ga teglarMatni(sanoq) — Map dan #bozor — 2, #uy — 1 satri. render() oxirida qatorni yangilang.
Yechim

HTML:

html
<p id="hisob" aria-live="polite"></p>
<p id="teglar" hidden></p>

"2. Sahifa elementlari":

js
const teglarQatori = document.getElementById("teglar");

VazifalarRoyxati klassidan oldin:

js
function teglariniOl(matn) {
  return matn
    .split(" ")
    .filter((soz) => soz.length > 1 && soz.startsWith("#"));
}

VazifalarRoyxati ichida, matnBormi dan keyin:

js
tegHisobi() {
  return this.#vazifalar
    .flatMap((vazifa) => teglariniOl(vazifa.matn))
    .reduce(
      (sanoq, teg) => sanoq.set(teg, (sanoq.get(teg) ?? 0) + 1),
      new Map(),
    );
}

Nega metod? Teglar — vazifalar to'plami haqidagi savol, #vazifalar esa faqat klass ichida ko'rinadi (OOP tamoyillari va kompozitsiya). holat.vazifalar — massiv emas, VazifalarRoyxati: unda flatMap yo'q, global funksiyadan holat.vazifalar.flatMap(...) deb yozsak — TypeError.

"3. Hisoblovchi funksiyalar":

js
function teglarMatni(sanoq) {
  return [...sanoq]
    .map(([teg, soni]) => `${teg} — ${soni}`)
    .join(", ");
}

render() oxirida, hisob qatoridan ("2 ta vazifa qoldi, 1 ta bajarildi") keyin:

js
const sanoq = holat.vazifalar.tegHisobi();
teglarQatori.hidden = sanoq.size === 0;
matnniYoz(teglarQatori, `Teglar: ${teglarMatni(sanoq)}`);

reduce callback'i sanoq.set(...) ni qaytaradi — set esa Map ning o'zini qaytaradi. Shuning uchun akkumulyator har qadamda o'sha bitta Map bo'lib qoladi (reduce darsida "akkumulyator Map ham bo'lishi mumkin" degan edik). Nom — sanoq, hisob emas: hisob — sahifadagi element, bir xil nom uni soyalab qo'yardi (TDZ darsidagi tuzoq).

Funksiyalarni avval Node'da sinang — brauzersiz, aniq ma'lumot bilan. Klass shart emas: tegHisobi ni oddiy funksiya qilamiz, this.#vazifalar o'rniga — vazifalar parametri. Ichi bir xil:

js
function teglariniOl(matn) {
  return matn
    .split(" ")
    .filter((soz) => soz.length > 1 && soz.startsWith("#"));
}

function tegHisobi(vazifalar) {
  return vazifalar
    .flatMap((vazifa) => teglariniOl(vazifa.matn))
    .reduce(
      (sanoq, teg) => sanoq.set(teg, (sanoq.get(teg) ?? 0) + 1),
      new Map(),
    );
}

const vazifalar = [
  { matn: "Non olish" },
  { matn: "Sabzi olish #bozor #uy" },
  { matn: "Guruch olish #bozor" },
  { matn: "Kitob # o'qish" },
];

console.log(teglariniOl("Sabzi olish #bozor #uy"));
console.log(tegHisobi(vazifalar));
console.log(tegHisobi([]).size);

Konsolda:

text
[ '#bozor', '#uy' ]
Map(2) { '#bozor' => 2, '#uy' => 1 }
0

Yolg'iz # teg hisoblanmadi — soz.length > 1 sharti shuning uchun. Bo'sh ro'yxatda Map bo'sh, size — 0, qator yashirinadi.

Node'da vazifalar — oddiy obyektlar, ilovada esa Vazifa klassining nusxalari (Private maydon va metodlar). Farqi yo'q: teglariniOl faqat vazifa.matn ni o'qiydi, klassda esa u getter orqali o'qiladi. Teglar matnning bir qismi bo'lgani uchun eksport formati ham o'zgarmaydi — { versiya: 1, vazifalar } dagi matn ular bilan birga yoziladi (JSON asoslari).

Brauzerda tekshiring:

  • Ochilganda teg yo'q — yangi qator ko'rinmaydi, qolgan hamma narsa avvalgidek.
  • "Sabzi olish #bozor #uy" qo'shing — Teglar: #bozor — 1, #uy — 1.
  • "Guruch olish #bozor" qo'shing — #bozor — 2. Uni o'chiring — yana 1.
  • "Sabzi…" ni tahrirlab, teglarni olib tashlang va saqlang — qator yashirinadi.

Commit: Vazifalar: teglar hisobi (Map). Branch'ni hali main ga qo'shmang — keyingi ikki darsda teglar yaxshilanadi.

12. Real ishda

  • Kesh. "So'rov → javob", "foydalanuvchi id → profil" jadvallari deyarli doim Map. Memoization darsida keshni Map bilan qayta yozamiz.
  • Statistika. Analitika va admin panellarda "sahifa → ko'rishlar soni", "mahsulot → sotuvlar" — bugungi sanash qolipi. Server loglarini tahlil qiluvchi skriptlarda ham shunday.
  • DOM bilan. Map kaliti DOM elementi bo'lishi mumkin: "tugma → uning sozlamasi". Element o'chganda xotira qanday tozalanishini WeakMap darsida ko'ramiz.
  • Xavfsizlik. Kod ko'rib chiqishda tashqaridan keladigan kalitlar oddiy obyektga yozilsa, tajribali dasturchi Map yoki Object.create(null) ni so'raydi — prototype pollution himoyasi.
  • Intervyu. "Map va obyektning farqi nima?" — eng ko'p beriladigan savollardan. Javob: kalit turi, tartib, size, prototip yo'qligi, JSON'ga yozilmasligi.

Xulosa

  • Map — kalit-qiymat to'plami: set, get, has, delete, clear, size. set Map ni qaytaradi — zanjir qilish mumkin.
  • Kalit istalgan tur va satrga aylantirilmaydi: 3 va "3" — ikki kalit. Obyektlar havola bo'yicha solishtiriladi, NaN o'ziga teng.
  • Tartib — qo'shilgan tartib. for...of juftlik beradi, keys/values/entries — iterator, forEach da avval qiymat, keyin kalit.
  • JSON Map ni {} qilib yozadi — oldin [...map] yoki Object.fromEntries(map).
  • Kalitlari tashqaridan keladigan lug'at — Map: unda meros kalitlar va __proto__ tuzog'i yo'q.

Keyingi dars: Set — faqat takrorlanmas qiymatlarni saqlaydigan to'plam: dublikatlarni bir qatorda olib tashlaymiz va "bormi?" savoliga tez javob beramiz.

Manbalar

  • MDN: "Map", "Map.groupBy()", "Map.prototype.getOrInsert()" — developer.mozilla.org
  • ECMAScript 2025 Language Specification: "Map Objects", "SameValueZero" — tc39.es/ecma262
  • web-features 3.40: map, array-group, getorinsert — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Map: istalgan turdagi kalitli lug'at — IlmHamroh