IlmHamroh
JavaScript Full-stack/10-qism. JavaScript chuqur2/65-dars21 daqiqa
Mundarija (31)

JavaScript map metodi: massivning har elementini o'zgartirish

Qisqacha: map massivning har elementi uchun callback'ni chaqiradi va callback qaytargan qiymatlardan yangi massiv yasaydi: narxlar.map((n) => n * 2). Yangi massiv doim asl massiv bilan bir xil uzunlikda bo'ladi, asl massiv esa o'zgarmaydi. "Har birini boshqa ko'rinishga aylantir" degan har vazifa — map ning ishi.

Bu darsda

  • map bilan sonlar, satrlar va obyektlar massivini aylantira olasiz.
  • map va forEach farqini aniq aytib bera olasiz.
  • Massivdagi bitta obyektni asl ma'lumotga tegmasdan yangilay olasiz.
  • ["1", "2", "3"].map(parseInt) nega [1, NaN, NaN] berishini tushuntira olasiz.
  • vazifalar dagi ro'yxat chizish siklini map bilan qayta yozasiz.

Oldin bilishingiz kerak: Callback imzosi va HOF metodlari, Function expression va arrow funksiyalar, Obyekt bo'ylab yurish, Spread va rest operatori, Sayoz va chuqur nusxalash.

1. Nega bu kerak?

Massiv bo'ylab yurish darsida savatdagi mahsulotlarni katta harfga o'tkazgan edik. forEach natija qaytarmagani uchun shunday yozdik:

js
const savat = ["non", "sut"];
const kattaHarfli = [];

for (const m of savat) {
  kattaHarfli.push(m.toUpperCase());
}

console.log(kattaHarfli); // [ 'NON', 'SUT' ]

O'sha darsda "keyinchalik buni map bitta qatorda qiladi" degan edik. 09-qismdagi vazifalar ning render() funksiyasida ham shu qolip bor: har vazifadan bitta li yasab, qutiga qo'shamiz.

Bu qolipning tuzilishi doim bir xil:

  1. Bo'sh massiv yaratiladi.
  2. Har element olinadi va boshqa ko'rinishga aylantiriladi.
  3. Natija yangi massivga push qilinadi.

O'zgaradigan qismi faqat ikkinchi qadam — "nimaga aylantiramiz". Qolgani har safar takrorlanadi. Hayotdan o'xshatish: tandir. Nonvoy tandirga 10 ta xamir yopadi — 10 ta non uzib oladi. Har xamir nonga aylanadi, soni esa o'zgarmaydi. map — shunday tandir: siz faqat "xamirdan non qanday chiqadi" degan qoidani berasiz. Bitta farqi bor: tandirda xamir tugaydi, map da esa asl massiv joyida qoladi — nonlar yangi savatga tushadi.

Bu darsda map ni avval sonlarda, keyin obyektlar va DOM elementlarida ishlatamiz. Oxirida vazifalar ning render() funksiyasidagi siklni ham map ga o'tkazamiz. Callback imzosi darsidagi bilim bu yerda ham kerak bo'ladi: map callback'ni o'sha uchta argument bilan chaqiradi va bu bitta mashhur tuzoqqa olib keladi.

2. Birinchi map

2.1 Sonlarni aylantirish

«Bahor»da mijozlar ko'pincha ikki porsiya olishadi. Har taomning ikki porsiyalik narxini hisoblaymiz:

js
const narxlar = [35000, 28000, 30000];
const ikkiPorsiya = narxlar.map((narx) => narx * 2);

console.log(ikkiPorsiya); // [ 70000, 56000, 60000 ]
console.log(narxlar); // [ 35000, 28000, 30000 ]

Callback (narx) => narx * 2 har elementni oladi va yangi qiymat qaytaradi. map shu qaytgan qiymatlarni tartib bilan yangi massivga yig'adi. Ikkinchi qatorga qarang: asl narxlar o'zgarmadi.

Bu ish transformatsiya (transformation) deyiladi — har elementni boshqa ko'rinishga aylantirish. Matematikadagi "har x ga 2x mos keladi" g'oyasining o'zi.

Endi qadamma-qadam ko'ring. Keyingi tugmasini bosib, yangi massiv qanday to'lib borishini kuzating:

2.2 map ning uchta va'dasi

map har doim uchta narsani kafolatlaydi:

Va'da Ma'nosi
Yangi massiv Natija — boshqa massiv, ikkiPorsiya === narxlar — false
Uzunlik bir xil 3 ta element kirdi — 3 ta chiqadi, bittasi ham tushib qolmaydi
Tartib saqlanadi 0-elementning natijasi 0-o'rinda, 1-niki 1-o'rinda

Ikkinchi va'da muhim. map element tanlamaydi. Callback biror elementda hech narsa qaytarmasa ham, o'sha o'ringa undefined tushadi — element yo'qolmaydi. Elementlarning bir qismini tanlash kerak bo'lsa, bu boshqa metodning ishi — keyingi darsdagi filter.

2.3 Satrlarni aylantirish va indeks

Callback istalgan turdagi qiymat qaytara oladi. Son kirib, satr chiqishi mumkin:

js
const narxlar = [35000, 28000, 30000];
const yozuvlar = narxlar.map((narx) => `${narx} so'm`);

console.log(yozuvlar); // [ "35000 so'm", "28000 so'm", "30000 so'm" ]

Callback imzosi darsidagi qoida map da ham amal qiladi: ikkinchi parametr — indeks. Menyuni raqamlaymiz va join bilan bitta matnga ulaymiz (Kesish, birlashtirish va matnga aylantirish):

js
const taomlar = ["Osh", "Lag'mon", "Manti"];
const qatorlar = taomlar.map((taom, i) => `${i + 1}. ${taom}`);

console.log(qatorlar.join("\n"));

Konsolda:

text
1. Osh
2. Lag'mon
3. Manti

Avval map har taomni raqamli satrga aylantirdi, keyin join("\n") ularni yangi qator belgisi bilan uladi. Telegram bot menyu xabarini aynan shunday yig'adi.

O'zingiz to'ldiring. [1, 2, 3].map((x) => x * 10) natijasi — [10, 20, ], uning uzunligi — .

Tekshirib ko'ring: const a = [4, 9]; const b = a.map((x) => x + 1); dan keyin a va b da nima bor? a === b nimaga teng?

Javob

a — [4, 9] (o'zgarmadi), b — [5, 10]. a === b — false: map doim yangi massiv yasaydi, ular ikki xil massiv.

2.4 map o'rtada to'xtamaydi

find mos elementni topishi bilan to'xtardi. map esa doim oxirigacha boradi: callback aniq length marta chaqiriladi. Bu uning va'dasidan kelib chiqadi — har elementga natija kerak, bittasini ham tashlab ketib bo'lmaydi.

js
const narxlar = [35000, 28000, 30000];

const yangi = narxlar.map((narx, i) => {
  console.log(`${i}-element: ${narx}`);
  return narx + 2000;
});

console.log(yangi); // [ 37000, 30000, 32000 ]

Konsolda:

text
0-element: 35000
1-element: 28000
2-element: 30000
[ 37000, 30000, 32000 ]

Callback ichidagi console.log — faqat kuzatish uchun, xuddi Massivda qidirish darsidagi find tajribasi kabi. Haqiqiy kodda map callback'i faqat qiymat qaytaradi.

break yozib bo'lmaydi: callback — alohida funksiya, sikl esa map ichida yashiringan (Massiv bo'ylab yurish darsidagi forEach bilan bir xil sabab). Erta to'xtash kerak bo'lsa, demak sizga map emas, find, some yoki for...of kerak.

2.5 Nomli callback yoki joyida arrow?

Callback'ni ikki xil berish mumkin. Qisqa va bir martalik bo'lsa — joyida, arrow bilan: narxlar.map((narx) => narx * 2). Uzun bo'lsa yoki bir necha joyda kerak bo'lsa — alohida nomli funksiya qilib, nomini bering: menyu.map(taomQatori).

Nomli funksiyaning uchta foydasi bor:

  • O'qiladi. menyu.map(taomQatori) — "menyuni taom qatorlariga aylantir" deb o'qiladi. Nomdagi so'z callback ichini o'qishdan tezroq tushuntiradi.
  • Qayta ishlatiladi. Bitta taomQatori menyuni ham, "kun taomlari" ni ham chizadi.
  • Alohida sinaladi. Funksiyani bitta obyekt bilan chaqirib, natijasiga qarash mumkin — butun massivsiz.

Nom qoidasi o'sha: qiymat qaytaradigan funksiya — natija nomi yoki fe'l bilan (taomQatori, narxniHisobla). Callback'ning o'ziga ham bu qoida tegishli: map dan oldin "bu funksiya bitta elementdan nima yasaydi?" deb so'rang — javob uning nomi bo'ladi.

3. Obyektlar massivi bilan map

Real ma'lumot ko'pincha obyektlar massivi: menyu, buyurtmalar, vazifalar. map ular bilan uch xil ish qiladi.

3.1 Bitta maydonni ajratib olish

Menyudan faqat taom nomlari kerak:

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

const nomlar = menyu.map((taom) => taom.nom);
console.log(nomlar.join(", ")); // Osh, Lag'mon, Manti, Ko'k choy

Obyekt kirdi — satr chiqdi. Bu eng ko'p uchraydigan map: ro'yxatdagi obyektlardan bitta maydon "sug'urib" olinadi.

Bir qatorli callback'ni destructuring bilan ham yozsa bo'ladi: menyu.map(({ nom }) => nom). Parametrda destructuring — Parametrda destructuring darsidagi usul. Qaysi biri o'qishga qulay bo'lsa — shuni tanlang.

Tekshirib ko'ring: Yuqoridagi menyu dan menyu.map((taom) => taom.narx > 10000) nima qaytaradi? Uzunligi nechta?

Javob

[ true, true, true, false ] — to'rt element. Callback mantiqiy qiymat qaytardi, map esa uni shunchaki yangi massivga qo'ydi. map tanlamaydi: arzon ko'k choy tushib qolmadi, uning o'rnida false turibdi. "Faqat qimmatlarini ol" degan vazifa — filter ning ishi.

3.2 Yangi obyekt yasash

Otabekka kechki aksiya uchun menyu kerak: har taomning eski va yangi narxi. Asl menyuga tegmaslik shart — kassa undan foydalanadi.

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Manti", narx: 30000 },
];

const aksiya = menyu.map((taom) => ({
  ...taom,
  aksiyaNarxi: taom.narx - taom.narx / 10,
}));

console.log(aksiya);
console.log(menyu[0]);

Konsolda:

text
[
  { nom: 'Osh', narx: 35000, aksiyaNarxi: 31500 },
  { nom: 'Manti', narx: 30000, aksiyaNarxi: 27000 }
]
{ nom: 'Osh', narx: 35000 }

Ikki narsaga e'tibor bering:

  • ...taom — eski obyektning hamma xususiyati yangi obyektga ko'chirildi (Spread), keyin yangi maydon qo'shildi.
  • => ({ ... }) — obyekt oddiy qavsga o'ralgan. Arrow funksiyalar darsidagi qoida: qavssiz => { ni JavaScript funksiya tanasi deb o'ylaydi. Bu xatoni «Ko'p uchraydigan xatolar» bo'limida ko'ramiz.

Oxirgi qator: asl menyu[0] da aksiyaNarxi yo'q. Callback imzosi darsida forEach bilan menyuni buzib qo'ygan edik — map bilan esa yangi obyektlar yasadik.

3.3 Ehtiyot bo'ling: map ham obyektni buzishi mumkin

map yangi massiv yasaydi, lekin ichidagi obyektlarni o'zi nusxalamaydi. Callback obyektni o'zgartirib, o'shani qaytarsa — nima bo'ladi?

js
const menyu = [{ nom: "Osh", narx: 35000 }];

const aksiya = menyu.map((taom) => {
  taom.narx = taom.narx - taom.narx / 10;
  return taom;
});

console.log(menyu[0].narx); // 31500
console.log(aksiya[0] === menyu[0]); // true

Massiv yangi, lekin uning ichidagi obyekt — o'sha-o'sha. Callback havola orqali asl obyektni o'zgartirdi (Havola semantikasi). Natijada ikkala massivda ham osh 31 500.

Qoida: map callback'i elementni o'zgartirmasin, yangisini qaytarsin — ({ ...taom, narx: ... }). Callback toza funksiya bo'lsin (Pure funksiya).

3.4 Bitta elementni yangilash (immutable)

Sayoz va chuqur nusxalash darsida savatdagi bitta mahsulotni yangilash uchun avval massiv nusxasini oldik, keyin kerakli o'ringa yangi obyekt qo'ydik. O'sha darsda "qisqaroq usul — map" degan edik. Mana u:

js
const vazifalar = [
  { id: 1, matn: "Non olish", bajarildi: false },
  { id: 2, matn: "DOM darsini takrorlash", bajarildi: false },
];

const yangilari = vazifalar.map((v) =>
  v.id === 2 ? { ...v, bajarildi: true } : v,
);

console.log(yangilari[1].bajarildi); // true
console.log(vazifalar[1].bajarildi); // false
console.log(yangilari[0] === vazifalar[0]); // true

Callback har vazifaga bitta savol beradi: "sen 2-raqamlimisan?" Ha bo'lsa — o'zgargan yangi obyekt qaytadi. Yo'q bo'lsa — eski obyektning o'zi (ternary operator).

Oxirgi qator true — bu xato emas, afzallik. O'zgarmagan vazifa nusxalanmadi, ikkala massivda umumiy qoldi. Asl vazifalar esa butunlay o'z holida.

Bu usulning nomi bor. Immutable yangilash (immutable update) — asl ma'lumotga tegmay, o'zgarishi bor yangi nusxa yasash. Inglizcha "immutable" — "o'zgarmas": eski qiymat o'zgarmaydi, uning o'rniga yangisi paydo bo'ladi. Xuddi daftardagi xatoni o'chirmay, yangi betga to'g'ri variantni yozishdek. React'da holatni aynan shunday yangilaysiz (React — 17-qismda, hozir bilish shart emas).

Tekshirib ko'ring: Yuqoridagi kodda callback v ni emas, { ...v } ni qaytarsa (o'zgarmagan vazifalar uchun ham), natija qanday farq qiladi?

Javob

Qiymatlar bir xil bo'ladi, lekin yangilari[0] === vazifalar[0] — false. O'zgarmagan vazifa ham keraksiz nusxalanadi. Kichik ro'yxatda farq sezilmaydi, lekin "nima o'zgardi?" degan savolga === endi javob bera olmaydi — har obyekt "yangi" bo'lib ko'rinadi.

3.5 Ichma-ich ma'lumot: map ichida map

Ba'zan har element ichida yana massiv bo'ladi. Masalan, har stolning buyurtmasi — taomlar ro'yxati. Rustam aka oshxonada faqat stol raqami va taom nomlarini ko'rishi kerak:

js
const stollar = [
  { stol: 3, taomlar: [{ nom: "Osh" }, { nom: "Ko'k choy" }] },
  { stol: 7, taomlar: [{ nom: "Manti" }] },
];

const oshxonaga = stollar.map((s) => ({
  stol: s.stol,
  nomlar: s.taomlar.map((t) => t.nom),
}));

console.log(oshxonaga);

Konsolda:

text
[
  { stol: 3, nomlar: [ 'Osh', "Ko'k choy" ] },
  { stol: 7, nomlar: [ 'Manti' ] }
]

Tashqi map har stolni yangi obyektga aylantiradi. Ichki map shu stolning taomlarini nomlarga aylantiradi. Har qavatda qoida o'sha: nechta kirdi — shuncha chiqdi. Stollar 2 ta — natija ham 2 ta, 3-stolning taomlari 2 ta — nomlari ham 2 ta.

Ikki qavatdan chuqurroq map o'qishni qiyinlashtiradi. Bunday paytda ichki qismni nomli funksiyaga chiqaring: nomlar: taomNomlari(s.taomlar).

4. Obyektni map bilan qayta ishlash

map — massiv metodi, obyektda yo'q. Lekin Obyekt bo'ylab yurish darsida obyektni massivga aylantirib, qayta yig'ishni o'rgangan edik: entries → o'zgartirish → fromEntries. O'sha darsda "map bilan bu bitta qatorga tushadi" degan edik:

js
const menyu = { osh: 35000, lagmon: 28000, choy: 5000 };

const chegirmali = Object.fromEntries(
  Object.entries(menyu).map(([nom, narx]) => [nom, narx * 0.9]),
);

console.log(chegirmali); // { osh: 31500, lagmon: 25200, choy: 4500 }
console.log(menyu); // { osh: 35000, lagmon: 28000, choy: 5000 }

Ichkaridan tashqariga o'qing:

  1. Object.entries(menyu) — [["osh", 35000], ...] juftliklar massivi.
  2. .map(([nom, narx]) => [nom, narx * 0.9]) — har juftlik yangi juftlikka aylanadi. Parametrdagi [nom, narx] — juftlikni darhol ikki o'zgaruvchiga ajratadi (Massiv destructuring).
  3. Object.fromEntries(...) — yangi juftliklardan yangi obyekt.

08-qismdagi sikl, bo'sh massiv va push kerak bo'lmadi.

5. map va forEach: qaysi biri qachon

Ikkalasi ham har element uchun callback chaqiradi. Farq — natijada:

forEach map
Qaytaradi undefined yangi massiv
Callback qaytargani tashlab yuboriladi yangi massivga tushadi
Maqsad yon ta'sir: chiqarish, DOM yangi ma'lumot yasash

Qoida oddiy: natija kerak bo'lsa — map, faqat ish bajarish kerak bo'lsa — forEach.

Ba'zan map ni forEach o'rnida ishlatishadi: taomlar.map((t) => console.log(t)). Kod ishlaydi, lekin map keraksiz [undefined, undefined, ...] massivini yasab, tashlab yuboradi. Eng yomoni — kodni o'qigan odam chalg'iydi: "map ekan, natijasi qayerda ishlatilyapti?" Kodni tekshiradigan vositalar (masalan ESLint, 16-qismda) bunday yozuvni xato deb belgilashi mumkin.

map kerak emas bo'lgan yana uch holat bor. Natija uzunligi boshqacha bo'lishi kerak bo'lsa (ba'zi elementlar tushib qolsin) — filter. Natija massiv emas, bitta son yoki obyekt bo'lsa (masalan jami summa) — reduce. Topish bilan to'xtash kerak bo'lsa — find. Har birini o'z darsida ko'ramiz. Belgi oddiy: "kirganlar soni = chiqqanlar soni va har biri aylanadi" — shundagina map.

Tekshirib ko'ring: Quyidagi ikki vazifaning qaysi biriga map, qaysi biriga forEach kerak? (a) har mijozga SMS yuborish; (b) mijozlar ro'yxatidan telefon raqamlari ro'yxatini olish.

Javob

(a) — forEach: SMS yuborish — yon ta'sir, natija massivi kerak emas. (b) — map: har mijoz telefon raqamiga aylanadi, natija — yangi massiv.

6. Brauzerda: map bilan elementlar yasash

map faqat son va satr bilan emas, DOM elementlari bilan ham ishlaydi. Callback element yaratib qaytarsa — natija elementlar massivi bo'ladi. Uni spread bilan replaceChildren ga bersa, hammasi bitta amal bilan sahifaga tushadi (Element yaratish, joylash va o'chirish):

html
<style>
  body { margin: 1rem; font: 1rem/1.5 system-ui, sans-serif; }
  ul { max-width: 18rem; padding: 0; list-style: none; }
  li {
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid #c9d6cf;
    padding-block: 0.25rem;
  }
  .narx { color: #b4461a; font-variant-numeric: tabular-nums; }
</style>
<h2>«Bahor» menyusi</h2>
<ul id="menyu"></ul>
<script>
  const menyu = [
    { nom: "Osh", narx: 35000 },
    { nom: "Lag'mon", narx: 28000 },
    { nom: "Manti", narx: 30000 },
    { nom: "Ko'k choy", narx: 5000 },
  ];
  const royxat = document.getElementById("menyu");

  function taomQatori(taom) {
    const li = document.createElement("li");
    const nom = document.createElement("span");
    nom.className = "taom-nomi";
    nom.textContent = taom.nom;
    const narx = document.createElement("span");
    narx.className = "narx";
    narx.textContent = `${taom.narx} so'm`;
    li.append(nom, narx);
    return li;
  }

  const qatorlar = menyu.map(taomQatori);
  royxat.replaceChildren(...qatorlar);
  console.log(qatorlar.length);
  console.log(royxat.children.length);
</script>

Konsolda:

text
4
4

menyu.map(taomQatori) — to'rtta obyekt kirdi, to'rtta li chiqdi. taomQatori ni qavssiz, to'g'ridan-to'g'ri berdik. Bu xavfsiz, chunki u faqat bitta parametr oladi — map beradigan indeks va massiv shunchaki e'tiborsiz qoladi. Matnlar textContent bilan yozildi (XSS xavfi).

"Tahrirlab ko'rish" bilan menyuga yangi obyekt qo'shing — ro'yxat va ikkala son ham o'zi o'zgaradi.

7. Mashhur tuzoq: ["1", "2", "3"].map(parseInt)

Inputdan kelgan satrlarni songa aylantirmoqchimiz. Matn ↔ son darsidagi parseInt ni to'g'ridan-to'g'ri beramiz:

js
console.log(["1", "2", "3"].map(parseInt)); // [ 1, NaN, NaN ]

Faqat birinchisi to'g'ri. Xato yo'q, ogohlantirish yo'q — shunchaki noto'g'ri natija. Nega?

Javob — callback imzosida. map parseInt ni uchta argument bilan chaqiradi: element, indeks, massiv. parseInt esa ikkinchi argumentni radix (sanoq tizimi asosi) deb oladi. Demak indeks radix'ga aylanib qoldi:

Chaqiruv Aslida Natija Nega
1-element parseInt("1", 0) 1 radix 0 — "berilmagan" deb olinadi, o'nlik
2-element parseInt("2", 1) NaN 1 lik sanoq tizimi yo'q (2 dan 36 gacha)
3-element parseInt("3", 2) NaN ikkilik tizimda 3 raqami yo'q

Yana bir misol buni yaqqol ko'rsatadi:

js
console.log(["10", "10", "10"].map(parseInt)); // [ 10, NaN, 2 ]

Uchinchi "10" ikkilik tizimda o'qildi — bu 2 (Son yozish shakllari).

Ikki tuzatish:

js
console.log(["1", "2", "3"].map(Number)); // [ 1, 2, 3 ]
const sonlar = ["1", "2", "3"].map((s) => parseInt(s, 10));
console.log(sonlar); // [ 1, 2, 3 ]

Number faqat bitta argumentdan foydalanadi — indeks unga xalaqit bermaydi. Ikkinchi usul radix'ni o'zimiz beradi va arrow funksiya indeksni "yutib" qo'yadi. Callback imzosi darsidagi qoidaning eng mashhur namunasi shu: ikkinchi parametri bor funksiyani metodga to'g'ridan-to'g'ri bermang.

Tekshirib ko'ring: ["1.5", "2.5"].map(parseFloat) to'g'ri ishlaydimi?

Javob

Ha: [ 1.5, 2.5 ]. parseFloat faqat bitta parametr oladi — radix'i yo'q (Matn ↔ son). Indeks unga beriladi, lekin e'tiborsiz qoladi. Shunga qaramay, odat sifatida arrow bilan yozish xavfsizroq: keyin kim funksiyani o'zgartirsa ham, kod buzilmaydi.

8. Ko'p uchraydigan xatolar

8.1 Jingalak qavsli callback'da return yo'q

js
const narxlar = [35000, 28000];

const ikkiPorsiya = narxlar.map((narx) => {
  narx * 2;
});

console.log(ikkiPorsiya); // [ undefined, undefined ]

Xato xabari yo'q, lekin hamma element undefined. Uzunlik saqlandi — map va'dasini bajardi. Callback esa hech narsa qaytarmadi. Tuzatish: (narx) => narx * 2 yoki { return narx * 2; }.

8.2 Obyektni qavssiz qaytarish

js
const menyu = [{ nom: "Osh", narx: 35000 }];

console.log(menyu.map((t) => { nom: t.nom })); // [ undefined ]

JavaScript { ni funksiya tanasi deb o'qidi. Ichidagi nom: esa obyekt kaliti emas, kamdan-kam ishlatiladigan label bo'lib qoldi (break, continue va label'lar). Natija — undefined. Ikki maydon yozsangiz, kod umuman ishga tushmaydi:

js
const menyu = [{ nom: "Osh", narx: 35000 }];

console.log(menyu.map((t) => { nom: t.nom, narx: t.narx }));
text
SyntaxError: Unexpected token ':'

Tarjimasi: "kutilmagan belgi: :". JavaScript ikkinchi narx: ni hech qanday qoidaga moslay olmadi. Tuzatish: obyektni qavsga o'rang — (t) => ({ nom: t.nom, narx: t.narx }).

8.3 Natijani saqlamaslik

js
const narxlar = [35000, 28000];

narxlar.map((narx) => narx * 2);

console.log(narxlar); // [ 35000, 28000 ]

"Narxlar ikki baravar bo'lishi kerak edi-ku!" map aslini o'zgartirmaydi — u yangi massivni qaytaradi. Qaytgan qiymat hech qayerga yozilmadi va yo'qoldi. Tuzatish: const ikkiPorsiya = narxlar.map(...).

8.4 NodeList da map yo'q

querySelectorAll qaytaradigan NodeList da forEach bor, lekin map va filter yo'q (Elementlarni tanlash). document.querySelectorAll("li").map(...) yozsangiz, Chrome konsolida:

text
Uncaught TypeError: document.querySelectorAll(...).map is not a function

Tarjimasi: "...map funksiya emas" — bunday metod umuman yo'q. Tuzatish: avval haqiqiy massivga aylantiring — [...document.querySelectorAll("li")].map(...).

9. Mashqlar

1-mashq (oson): Chek qatorlari

narxlar massividan map bilan 35000 so'm ko'rinishidagi satrlar massivini yasang va uni join(" | ") bilan chiqaring.

js
const narxlar = [35000, 28000, 5000];
Yechim
js
const narxlar = [35000, 28000, 5000];
const yozuvlar = narxlar.map((narx) => `${narx} so'm`);

console.log(yozuvlar.join(" | "));

Konsolda:

text
35000 so'm | 28000 so'm | 5000 so'm

Son kirdi — satr chiqdi. Uzunlik o'zgarmadi: 3 ta.

2-mashq (o'rta): Buyurtma summasi

Ofitsiant Otabek buyurtmalarni faqat taom nomi va soni bilan yozadi. Narx menyuda. Har buyurtmaga summa maydonini qo'shib, yangi massiv yasang.

Ishora: callback ichida narxni find bilan menyudan toping. Yangi obyekt — ({ ...b, summa: ... }).

js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Manti", narx: 30000 },
  { nom: "Ko'k choy", narx: 5000 },
];
const buyurtmalar = [
  { taom: "Osh", soni: 2 },
  { taom: "Ko'k choy", soni: 3 },
];
Yechim
js
const menyu = [
  { nom: "Osh", narx: 35000 },
  { nom: "Manti", narx: 30000 },
  { nom: "Ko'k choy", narx: 5000 },
];
const buyurtmalar = [
  { taom: "Osh", soni: 2 },
  { taom: "Ko'k choy", soni: 3 },
];

const hisoblangan = buyurtmalar.map((b) => {
  const taom = menyu.find((t) => t.nom === b.taom);
  return { ...b, summa: taom.narx * b.soni };
});

console.log(hisoblangan);

Konsolda:

text
[
  { taom: 'Osh', soni: 2, summa: 70000 },
  { taom: "Ko'k choy", soni: 3, summa: 15000 }
]

Callback jingalak qavsli, chunki ichida ikki qadam bor — shuning uchun return yozdik. Ikki HOF metod bir-birining ichida: map callback'i ichida find. Asl buyurtmalar o'zgarmadi.

3-mashq (qiyin): Savatda sonni o'zgartirish

sonniOzgartir(savat, nom, yangiSoni) funksiyasini yozing. U savatni o'zgartirmasdan, kerakli mahsulot soni yangilangan yangi savat qaytarsin. O'zgarmagan mahsulotlar eski obyektlar bo'lib qolsin. Uchta narsani tekshiring: yangi son, asl savat va ===.

Yechim
js
function sonniOzgartir(savat, nom, yangiSoni) {
  return savat.map((m) =>
    m.nom === nom ? { ...m, soni: yangiSoni } : m,
  );
}

const savat = [
  { nom: "Osh", soni: 1 },
  { nom: "Manti", soni: 2 },
];
const yangi = sonniOzgartir(savat, "Manti", 5);

console.log(yangi[1].soni); // 5
console.log(savat[1].soni); // 2
console.log(yangi[0] === savat[0]); // true
console.log(yangi === savat); // false

Funksiya toza: bir xil argumentga doim bir xil natija va hech narsani buzmaydi. Bunday funksiyani sinash oson — uchta console.log uni to'liq tekshirdi.

4-mashq: Vazifalar qadami — qatorlarni map bilan yasang

refactor/hof-metodlar branch'ida davom etamiz. render() ning boshi hozir shunday:

js
const quti = document.createDocumentFragment();
for (const vazifa of korinadiganlar()) {
  if (vazifa.id === holat.tahrirId) {
    quti.append(tahrirQatori(vazifa));
  } else {
    quti.append(oddiyQator(vazifa));
  }
}
royxat.replaceChildren(quti);

Uni ikki qismga ajrating:

  1. qatorYasa(vazifa) — vazifadan tayyor li qaytaradigan funksiya: tahrirdagisi uchun tahrirQatori, qolgani uchun oddiyQator.
  2. render() da — korinadiganlar() ni map bilan li larga aylantirib, replaceChildren ga bering.

Xulq o'zgarmasin.

Yechim

Yangi funksiya — "4. Chizish" bo'limiga, tahrirQatori dan keyin:

js
function qatorYasa(vazifa) {
  if (vazifa.id === holat.tahrirId) {
    return tahrirQatori(vazifa);
  }
  return oddiyQator(vazifa);
}

render() ning boshi endi bitta qator:

js
royxat.replaceChildren(...korinadiganlar().map(qatorYasa));

Nima o'zgardi:

  • DocumentFragment kerak emas. replaceChildren(...) ko'p elementni bitta amal bilan qo'yadi. DOM standartiga ko'ra brauzer ularni ichkarida o'zi fragmentga yig'adi — sahifa avvalgidek bir marta qayta chiziladi (Ko'p elementni samarali chizish).
  • qatorYasa — toza funksiya. U holat ni o'qiydi, lekin hech narsani o'zgartirmaydi: vazifa kiradi, li chiqadi. Bu — map callback'iga aynan kerakli shakl.
  • Qavssiz berish xavfsiz. map qatorYasa ni (vazifa, index, array) bilan chaqiradi, u esa faqat birinchisini oladi. Callback imzosi darsidagi tekshiruvni qildik: ikkinchi parametr yo'q — tuzoq yo'q.
  • Bo'sh ro'yxat. Filtr hech narsa qoldirmasa, map bo'sh massiv qaytaradi, replaceChildren() argumentsiz chaqiriladi va ro'yxatni tozalaydi — avvalgidek.

Xulq o'zgarmaganini tekshiring:

  • Sahifa ochilganda — uch vazifa, "2 ta vazifa qoldi".
  • «Tahrirlash» — qator maydonga aylanadi, Esc — qaytadi, fokus «Tahrirlash» da.
  • «Faol» filtrida ikkala vazifani belgilang — "Faol vazifa yo'q." chiqadi, fokus "Ro'yxat" sarlavhasida.

Commit: Render: qatorlarni map bilan yasa.

10. Real ishda

  • React. Ro'yxat chizishning yagona asosiy usuli: vazifalar.map((v) => <li>{v.matn}</li>). Bugungi menyu.map(taomQatori) — aynan shu g'oya, faqat qo'lda.
  • API javoblari. Serverdan kelgan ma'lumot ko'pincha boshqa shaklda: created_at, user_name. Uni ilovaga kerakli ko'rinishga map bilan aylantirishadi — bu qatlam "mapper" deb ham ataladi.
  • Immutable yangilash. list.map((x) => x.id === id ? { ...x, ... } : x) — React, Redux va boshqa holat kutubxonalarida har kuni yoziladigan qator.
  • Hisobotlar. Buxgalteriya jadvalidagi har qatorga yangi ustun qo'shish — "QQS bilan narx", "dollarda narx" — dasturda map bilan qilinadi. Jadval satrlari soni o'zgarmaydi, har satr yangi maydon oladi — 2-mashqdagi summa aynan shunday.
  • Intervyu. ["1", "2", "3"].map(parseInt) — klassik savol. Endi siz faqat javobni emas, sababini ham bilasiz: callback imzosi va radix.

Xulosa

  • map har elementni callback orqali aylantiradi va yangi massiv qaytaradi.
  • Yangi massiv — aslidek uzunlikda va tartibda; asl massiv o'zgarmaydi.
  • Obyekt qaytarishda qavs: => ({ ... }). Callback obyektni o'zgartirmasin — yangisini qaytarsin.
  • map((x) => x.id === id ? { ...x, ... } : x) — bitta elementni immutable yangilash.
  • Natija kerak — map, faqat ish bajarish — forEach. map o'rtada to'xtamaydi va element tanlamaydi.
  • Uzun callback'ni nomli funksiyaga chiqaring: menyu.map(taomQatori) o'zi o'qiladi.
  • Ikkinchi parametri bor funksiyani (parseInt) to'g'ridan-to'g'ri bermang — arrow bilan o'rang.

Keyingi dars: filter: shart bo'yicha tanlash — massivdan faqat kerakli elementlarni tanlaymiz va vazifalar dagi korinadiganlar ni bitta qatorga tushiramiz.

Manbalar

  • MDN: "Array.prototype.map()" — developer.mozilla.org
  • MDN: "Element: replaceChildren() method" — developer.mozilla.org
  • DOM Standard: "converting nodes into a node" — dom.spec.whatwg.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript map metodi: massivning har elementini o'zgartirish — IlmHamroh