Mundarija (32)
- Bu darsda
- 1. Nega bu kerak?
- 2. Birinchi reduce: yig'indi
- 2.1 Kod
- 2.2 Qadamma-qadam
- 2.3 reduce ichidan
- 2.4 To'liq imzo
- 3. Boshlang'ich qiymat
- 3.1 Uni yozmasa nima bo'ladi?
- 3.2 Bo'sh massiv: TypeError
- 3.3 Obyektlar massivi: [object Object]
- 4. reduce bilan istalgan natija
- 4.1 Buyurtma summasi: obyektlardan son
- 4.2 Sanoq: massivdan obyekt
- 4.3 Akkumulyatorni o'zgartirsa bo'ladimi?
- 4.4 Lug'at: id bo'yicha qidirish
- 4.5 Guruhlash va Map
- 5. reduceRight — o'ngdan chapga
- 6. Qachon reduce emas
- 7. Ko'p uchraydigan xatolar
- 7.1 Jingalak qavsda return yo'q
- 7.2 Parametrlar tartibi aralashgan
- 7.3 Boshlang'ich qiymatni unutish
- 7.4 Sanoqda akkumulyator o'rniga son qaytarish
- 8. Mashqlar
- 1-mashq (oson): Oylik xarajatlar
- 2-mashq (o'rta): Eng qimmat taom
- 3-mashq (qiyin): Vazifalar statistikasi
- 4-mashq: Vazifalar qadami — oxirgi sikl va branch'ni yakunlash
- 9. Real ishda
- Xulosa
- Manbalar
JavaScript reduce metodi: massivni bitta qiymatga yig'ish
Qisqacha:
reducemassivni bitta natijaga yig'adi: son, satr, obyekt — istalgan narsa. Callback har qadamda akkumulyator (hozirgacha yig'ilgan natija) va joriy elementni oladi va yangi akkumulyatorni qaytaradi:narxlar.reduce((jami, n) => jami + n, 0). Oxirgi0— boshlang'ich qiymat; uni doim yozing, aks holda bo'sh massivdaTypeErrorchiqadi.
Bu darsda
reducebilan yig'indi, eng katta qiymat va o'rtachani hisoblay olasiz.- Akkumulyator va boshlang'ich qiymat nima ekanini va nega boshlang'ich qiymatni doim yozish kerakligini tushuntira olasiz.
reducebilan sanoq va lug'at (obyekt) yasay olasiz.reduceRightni vareduceqachon kerak emasligini bilasiz.vazifalardagi oxirgifor...ofsiklini almashtirib, refaktoringni yakunlaysiz.
Oldin bilishingiz kerak: Callback imzosi va HOF metodlari, map, filter, Obyekt: kalit-qiymat juftliklari, Shorthand va computed kalitlar.
1. Nega bu kerak?
Massiv bo'ylab yurish darsida o'rtacha ballni hisoblagan edik. 08-qism yakunidagi oylik byudjet dasturida xarajatlarni qo'shdik. 09-qismdagi vazifalar da esa faol vazifalarni sanadik. Uchala kodning skeleti bir xil:
let jami = 0; // 1. boshlang'ich qiymat
for (const narx of narxlar) {
jami = jami + narx; // 2. eski natija + element = yangi
}
console.log(jami); // 3. oxirgi natijamap va filter bu yerda yordam bermaydi: ular doim massiv qaytaradi. Bizga esa bitta son kerak. Massivni bitta qiymatga "qisqartirish" — reduce ning ishi (inglizcha "reduce" — "kamaytirmoq, qisqartirmoq").
Hayotdan o'xshatish: kassa. Otabek chek uradi. Kassa ekranida "Jami: 0". Osh urildi — "Jami: 35 000". Lag'mon — "Jami: 63 000". Har mahsulotda kassa bitta ishni qiladi: eski jamiga yangi narxni qo'shib, ekranga yangi jamini yozadi. Oxirgi mahsulotdan keyin ekrandagi son — chekning summasi. reduce — o'sha kassa, ekrandagi "Jami" esa — akkumulyator.
reduce — bu oiladagi eng kuchli va eng ko'p qo'rqitadigan metod. Kuchli, chunki natija istalgan narsa bo'lishi mumkin: son, satr, obyekt. Qo'rqitadi, chunki callback'da birdaniga ikki "yuruvchi" qiymat bor — akkumulyator va element. Shuning uchun bu darsda sekin yuramiz: avval yig'indi qadamma-qadam, keyin reduce ning ichi, so'ng boshlang'ich qiymatning ikki tuzog'i. Undan keyin — sanoq va lug'at, oxirida esa eng muhim savol: reduce qachon kerak emas.
Bu dars vazifalar refaktoringini ham yakunlaydi. To'rtta for...of siklidan uchtasi forEach, map va filter ga o'tdi. Oxirgisi — faollarSoni — bugun reduce ga o'tadi.
2. Birinchi reduce: yig'indi
2.1 Kod
Mehmon osh, lag'mon va ikki ko'k choy oldi. Chek summasi:
const chek = [35000, 28000, 5000, 5000];
const jami = chek.reduce((summa, narx) => summa + narx, 0);
console.log(jami); // 73000reduce ga ikki narsa berildi:
- Callback —
(summa, narx) => summa + narx. Birinchi parametri — hozirgacha yig'ilgan natija, ikkinchisi — joriy element. U yangi natijani qaytaradi. - Boshlang'ich qiymat —
0. Birinchi qadamdasummashu bo'ladi.
Akkumulyator (accumulator) — reduce qadamdan qadamga olib o'tadigan "hozirgacha yig'ilgan natija". Inglizcha "accumulate" — "to'plamoq". Har qadamda callback qaytargan qiymat keyingi qadamning akkumulyatoriga aylanadi. Oxirgi qadamda qaytgani — reduce ning natijasi.
2.2 Qadamma-qadam
Bu darsning eng muhim joyi — akkumulyator har qadamda qanday o'zgarishi. Keyingi tugmasini bosib, summa qanday o'sishiga qarang:
Xuddi shu jarayon jadvalda:
| Qadam | summa (kirdi) |
narx |
Callback qaytardi |
|---|---|---|---|
| 1 | 0 |
35000 |
35000 |
| 2 | 35000 |
28000 |
63000 |
| 3 | 63000 |
5000 |
68000 |
| 4 | 68000 |
5000 |
73000 |
Har qatorning o'ng ustuni keyingi qatorning summa siga ko'chadi. Akkumulyatorni callback o'zi saqlamaydi — uni reduce olib yuradi. Callback faqat "eskisidan yangisini" hisoblaydi.
2.3 reduce ichidan
Oldingi darslarda forEach va filter ning nusxasini o'zimiz yozgan edik. reduce ning soddalashtirilgan nusxasi ham qisqa — va u «Nega bu kerak?» bo'limidagi sikl skeletining o'zi:
function yigish(massiv, amal, boshlangich) {
let akkumulyator = boshlangich;
for (let i = 0; i < massiv.length; i++) {
akkumulyator = amal(akkumulyator, massiv[i], i, massiv);
}
return akkumulyator;
}
const chek = [35000, 28000, 5000, 5000];
console.log(yigish(chek, (summa, narx) => summa + narx, 0)); // 73000Uchta narsaga qarang. let akkumulyator = boshlangich — skeletdagi let jami = 0. Sikl ichidagi tayinlash — callback qaytargan qiymat eski akkumulyatorning o'rnini egallaydi. Oxirgi return — natija. Callback'dagi summa + narx esa skeletdagi yagona o'zgaruvchan qism edi — endi u tashqaridan keladi.
Haqiqiy reduce da bitta qo'shimcha bor: boshlang'ich qiymat berilmasa, u birinchi elementni oladi va siklni 1-indeksdan boshlaydi. Bu qo'shimcha nimaga olib kelishini «Boshlang'ich qiymat» bo'limida ko'ramiz.
2.4 To'liq imzo
reduce ning callback'i Callback imzosi darsidagi qoidadan bitta farq qiladi — oldida akkumulyator turadi:
massiv.reduce(
(akkumulyator, element, index, array) => yangiAkkumulyator,
boshlangichQiymat,
);Odatda faqat birinchi ikkitasi kerak: akkumulyator va element. Indeks va massiv kamdan-kam ishlatiladi. Masalan, o'rtacha ballni ularsiz ham oson topasiz: avval reduce bilan yig'indi, keyin uzunlikka bo'lish.
const ballar = [78, 60, 91, 85];
const yigindi = ballar.reduce((jami, ball) => jami + ball, 0);
console.log(yigindi / ballar.length); // 78.5O'zingiz to'ldiring. [2, 3, 4].reduce((a, x) => a + x, 10) natijasi — [:19]. [2, 3, 4].reduce((a, x) => a * x, 1) natijasi — [:24].
Tekshirib ko'ring:
[5, 1, 8].reduce((eng, x) => (x > eng ? x : eng), 0)nima qaytaradi? Akkumulyator qanday o'zgaradi?
Javob
8. Akkumulyator: 0 → 5 (5 > 0) → 5 (1 > 5 emas, eski qoladi) → 8. Bu yerda akkumulyator "yig'indi" emas, "hozirgacha eng kattasi". Akkumulyator har qanday ma'noga ega bo'lishi mumkin — u callback qaytargan narsa.
3. Boshlang'ich qiymat
3.1 Uni yozmasa nima bo'ladi?
Boshlang'ich qiymat ixtiyoriy. Yozilmasa, reduce massivning birinchi elementini akkumulyator qilib oladi va callback'ni ikkinchi elementdan boshlaydi:
const chek = [35000, 28000, 5000];
const jami = chek.reduce((summa, narx, i) => {
console.log(`${i}-element: ${summa} + ${narx}`);
return summa + narx;
});
console.log(jami);Konsolda:
1-element: 35000 + 28000
2-element: 63000 + 5000
68000Callback ikki marta chaqirildi, 0-element esa tayyor akkumulyator bo'lib qoldi. Yig'indi uchun natija o'sha. Lekin ikkita xavfli holat bor.
3.2 Bo'sh massiv: TypeError
Bugun hali birorta buyurtma bo'lmagan. Kunlik tushum:
const bugungi = [];
const tushum = bugungi.reduce((summa, narx) => summa + narx);
console.log(tushum);TypeError: Reduce of empty array with no initial valueTarjimasi: "boshlang'ich qiymatsiz bo'sh massivni yig'ish". reduce akkumulyatorni birinchi elementdan olmoqchi edi — element yo'q. Nima qaytarishni bilmadi va xato tashladi. Dastur shu yerda to'xtaydi — kechqurun emas, ertalab, birinchi mijozdan oldin.
Boshlang'ich qiymat bilan esa hammasi tinch:
const bugungi = [];
const tushum = bugungi.reduce((summa, narx) => summa + narx, 0);
console.log(tushum); // 0Bo'sh massivda callback bir marta ham chaqirilmaydi — reduce boshlang'ich qiymatni o'zgarishsiz qaytaradi. "Hech narsa sotilmadi" — 0. To'g'ri javob.
3.3 Obyektlar massivi: [object Object]
Ikkinchi xavf jimgina ishlaydi. Menyudagi narxlarni qo'shamiz, boshlang'ich qiymatni unutib:
const menyu = [
{ nom: "Osh", narx: 35000 },
{ nom: "Lag'mon", narx: 28000 },
];
const jami = menyu.reduce((summa, taom) => summa + taom.narx);
console.log(jami); // [object Object]28000Son o'rniga g'alati satr! Birinchi akkumulyator — obyekt { nom: "Osh", ... } bo'ldi. Obyekt + son: JavaScript obyektni satrga aylantirdi — "[object Object]" — va 28000 ni unga yopishtirdi (Yashirin coercion). Xato xabari yo'q.
Sabab bitta: akkumulyator son bo'lishi kerak edi, element esa obyekt. Boshlang'ich qiymat yozilmasa, akkumulyator element turida boshlanadi. Tuzatish — 0 ni yozish: menyu.reduce((summa, taom) => summa + taom.narx, 0).
Diqqat: Qoida oddiy — boshlang'ich qiymatni doim yozing. U ikkala xavfni yo'qotadi va kodni o'qiganga natija turini darhol aytadi:
0— son yig'ilyapti,{}— obyekt,""— satr.
Tekshirib ko'ring:
[7].reduce((a, x) => a + x)nima qaytaradi? Callback necha marta chaqiriladi?
Javob
7, callback esa bir marta ham chaqirilmaydi. Boshlang'ich qiymat yo'q — birinchi element (7) akkumulyator bo'ldi. Ikkinchi element yo'q — yig'ish uchun qadam qolmadi. reduce akkumulyatorni o'zgarishsiz qaytardi.
4. reduce bilan istalgan natija
reduce ning kuchi — natija turi erkin. Akkumulyator son bo'lishi shart emas.
4.1 Buyurtma summasi: obyektlardan son
const savat = [
{ nom: "Osh", narx: 35000, soni: 2 },
{ nom: "Ko'k choy", narx: 5000, soni: 3 },
];
const jami = savat.reduce(
(summa, mahsulot) => summa + mahsulot.narx * mahsulot.soni,
0,
);
console.log(jami); // 85000Element — obyekt, akkumulyator — son. Ikkalasi har xil turda, va bu normal: boshlang'ich 0 akkumulyator turini belgilab qo'ydi.
Narxlar butun so'mda saqlangan — shuning uchun yig'indi aniq chiqdi. Tiyin yoki foiz bilan ishlaganda kasr sonlar xatosi yig'indida to'planib boradi; pulni butun sonlarda hisoblash qoidasi Pul va aniq hisob-kitob darsida.
Tekshirib ko'ring: Savatdagi mahsulotlarning umumiy soni (nechta porsiya) kerak. Yuqoridagi
savatuchun callback va boshlang'ich qiymat qanday bo'ladi?
Javob
savat.reduce((jami, m) => jami + m.soni, 0) — natija 5 (2 osh + 3 choy). Callback skeleti o'sha, faqat narx o'rniga soni qo'shiladi. Boshlang'ich qiymat yana 0, chunki natija — son.
4.2 Sanoq: massivdan obyekt
Jasur aka kun oxirida qaysi taom necha marta buyurtma qilinganini bilmoqchi:
const buyurtmalar = ["osh", "choy", "osh", "manti", "choy", "osh"];
const sanoq = buyurtmalar.reduce((natija, taom) => {
natija[taom] = (natija[taom] ?? 0) + 1;
return natija;
}, {});
console.log(sanoq); // { osh: 3, choy: 2, manti: 1 }Boshlang'ich qiymat — bo'sh obyekt {}. Har qadamda:
natija[taom]— shu taomning hozirgi soni. Taom birinchi marta uchrasa —undefined,?? 0uni0qiladi (??operatori).- Bittaga oshirib, o'sha kalitga yozamiz — kalit nomi o'zgaruvchidan (Computed kalitlar).
return natija— albatta. Callback qaytargani keyingi akkumulyator bo'ladi.returnunutilsa, keyingi qadamda akkumulyatorundefinedbo'ladi.
4.3 Akkumulyatorni o'zgartirsa bo'ladimi?
Callback imzosi darsida "callback ichida mutatsiya qilmang" degan edik. Bu yerda esa natija[taom] = ... — mutatsiya. Ziddiyat yo'qmi?
Yo'q. O'sha qoida asl massiv va uning elementlari haqida edi: ularni boshqa kod ham ishlatadi. Akkumulyator esa reduce boshlanishida {} dan yangi yaratilgan obyekt — u faqat shu reduce ga tegishli. Uni hech kim tashqaridan ko'rmaydi, demak o'zgartirish xavfsiz.
Ba'zi kodlarda har qadamda yangi obyekt yasashadi: ({ ...natija, [taom]: ... }). Natija bir xil, lekin har qadamda butun obyekt nusxalanadi. Ming elementli massivda bu ming marta nusxalash degani. Akkumulyatorni o'zgartirish — bu yerda to'g'ri va tez yo'l.
Diqqat: Boshlang'ich qiymat sifatida tashqi obyektni bermang:
buyurtmalar.reduce(..., kechagiSanoq). Shunda akkumulyator yangi emas —kechagiSanoqning o'zi o'zgarib ketadi (Havola semantikasi). Boshlang'ich qiymat har doim yangi bo'lsin:{},[],0.
4.4 Lug'at: id bo'yicha qidirish
Ro'yxatdan id bo'yicha tez topiladigan "lug'at" yasash — real loyihalarda ko'p uchraydigan ish:
const vazifalar = [
{ id: 1, matn: "Non olish" },
{ id: 3, matn: "Onamga qo'ng'iroq qilish" },
];
const idBoyicha = vazifalar.reduce((lugat, v) => {
lugat[v.id] = v;
return lugat;
}, {});
console.log(idBoyicha[3].matn); // Onamga qo'ng'iroq qilishNatija — obyekt, uning kalitlari id lar: { 1: {...}, 3: {...} }. Obyekt kalitlari doim satr bo'ladi, lekin idBoyicha[3] yozsangiz ham ishlaydi — JavaScript 3 ni o'zi "3" ga aylantiradi (Obyekt: kalit-qiymat juftliklari).
Endi idBoyicha[3] — find siz, darhol topiladi. find har safar ro'yxatni boshidan aylanadi; lug'atda esa kalit bo'yicha to'g'ridan-to'g'ri olinadi. Bir marta reduce qilib, keyin yuz marta qidirsangiz — farq sezilarli bo'ladi.
4.5 Guruhlash va Map
Taomlarni narx toifasi bo'yicha guruhlash ham reduce ning klassik ishi: { arzon: [...], qimmat: [...] }. Bu shunchalik ko'p kerak bo'ladiki, 2024-yilda JavaScript'ga buning uchun alohida funksiya qo'shildi — Object.groupBy. Uni Guruhlash darsida reduce bilan solishtirib ko'ramiz.
Akkumulyator oddiy obyekt emas, Map ham bo'lishi mumkin. Map — kalit-qiymat juftliklarini saqlaydigan maxsus to'plam, uni Map darsida o'rganamiz — hozir bilish shart emas. Asosiysi: akkumulyator turi — boshlang'ich qiymatniki.
Tekshirib ko'ring: Quyidagi kodda nima xato?
buyurtmalar.reduce((n, taom) => { n[taom] = (n[taom] ?? 0) + 1; }, {})
Javob
return n yo'q. Birinchi qadam to'g'ri ishlaydi, lekin callback undefined qaytaradi. Ikkinchi qadamda akkumulyator undefined bo'ladi va n[taom] — TypeError: Cannot read properties of undefined. Jingalak qavsli callback'da akkumulyatorni qaytarishni unutmang.
5. reduceRight — o'ngdan chapga
reduceRight xuddi reduce, faqat elementlarni oxiridan boshiga qarab yuradi:
const harflar = ["o", "s", "h"];
const chapdan = harflar.reduce((satr, h) => satr + h, "");
const ongdan = harflar.reduceRight((satr, h) => satr + h, "");
console.log(chapdan); // osh
console.log(ongdan); // hsoIkkalasi bir xil callback va bir xil boshlang'ich qiymat oldi. Farq faqat elementlar kelish tartibida: reduce "o", "s", "h" ni, reduceRight esa "h", "s", "o" ni navbat bilan qo'shdi. Satr uchun tartib muhim — natijalar ham har xil.
Yig'indida farq yo'q: 1 + 2 + 3 va 3 + 2 + 1 bir xil. Tartib faqat natija tartibga bog'liq bo'lganda muhim — satr yig'ish, ketma-ket amallar. Amalda reduceRight kam uchraydi; eski kodda ko'rsangiz, "teskari reduce" deb o'qing.
6. Qachon reduce emas
reduce shunchalik erkinki, uni hamma narsaga ishlatish mumkin — map ni ham, filter ni ham u bilan yozsa bo'ladi. Lekin bu yaxshi g'oya emas. Solishtiring — faol vazifalar matnlari:
const vazifalar = [
{ matn: "Non olish", bajarildi: false },
{ matn: "DOM darsini takrorlash", bajarildi: true },
];
const reduceBilan = vazifalar.reduce((natija, v) => {
if (!v.bajarildi) {
natija.push(v.matn);
}
return natija;
}, []);
const zanjirBilan = vazifalar
.filter((v) => !v.bajarildi)
.map((v) => v.matn);
console.log(reduceBilan); // [ 'Non olish' ]
console.log(zanjirBilan); // [ 'Non olish' ]Natija bir xil. Lekin ikkinchisini o'qigan odam darhol tushunadi: "tanla, keyin aylantir". Birinchisida esa akkumulyator, if, push va return ni o'qib, niyatni o'zi topishi kerak.
Bu — umumiy qoida. Kod kompyuter uchun bir marta yoziladi, odamlar uchun esa o'nlab marta o'qiladi: sizning hamkasbingiz, kod ko'rib chiquvchi, olti oydan keyingi o'zingiz. Ikki yozuv bir xil ishlasa, niyati tezroq ko'rinadigani yaxshiroq. map va filter nomining o'zi niyatni aytadi. reduce esa faqat "nimadir yig'ilyapti" deydi — nima yig'ilayotganini callback ichidan topish kerak.
Tekshirib ko'ring: "Menyudagi hamma taomlarning nomlari, vergul bilan" —
reducekerakmi?
Javob
Shart emas. menyu.map((t) => t.nom).join(", ") — aylantirish va join. reduce bilan ham bo'ladi ((satr, t) => satr + ", " + t.nom), lekin boshidagi ortiqcha vergulni alohida hal qilish kerak bo'ladi. Tayyor vosita bor bo'lsa — o'shani ishlating.
Tanlash uchun jadval:
| Vazifa | Yaxshiroq |
|---|---|
| Har birini aylantirish | map |
| Bir qismini tanlash | filter |
| "Bormi?", "hammasimi?" | some, every |
| Bitta elementni topish | find |
| Guruhlash | Object.groupBy (Guruhlash) |
| Yig'indi, sanoq, lug'at | reduce |
Yana bir chegara: callback ichida bir nechta if, ichma-ich obyektlar va o'n qator kod paydo bo'lsa — ehtimol oddiy for...of o'qishga osonroq. reduce — qisqa va aniq yig'ish uchun. Murakkab hisob-kitobni sikl bilan yozish uyat emas — asosiysi, keyingi o'quvchi uni tushunsin.
7. Ko'p uchraydigan xatolar
7.1 Jingalak qavsda return yo'q
const chek = [35000, 28000];
const jami = chek.reduce((summa, narx) => {
summa + narx;
}, 0);
console.log(jami); // undefinedBirinchi qadam undefined qaytardi. Ikkinchi qadamda undefined + 28000 — NaN, lekin u ham qaytarilmadi — natija undefined. Tuzatish: (summa, narx) => summa + narx yoki { return summa + narx; }.
7.2 Parametrlar tartibi aralashgan
const chek = [35000, 28000, 5000];
const jami = chek.reduce((narx, summa) => narx + summa, 0);
console.log(jami); // 68000Bu safar natija to'g'ri chiqdi — qo'shishda tartib ahamiyatsiz. Lekin nomlar yolg'on gapiryapti: narx aslida akkumulyator. Murakkabroq callback'da (masalan summa.narx) kod darhol buziladi. Qoida: birinchi parametr — doim akkumulyator. Nomini ham shunga mos qo'ying: jami, summa, natija, lugat.
7.3 Boshlang'ich qiymatni unutish
«Boshlang'ich qiymat» bo'limidagi ikki xato: bo'sh massivda TypeError: Reduce of empty array with no initial value va obyektlar bilan [object Object]28000. Tuzatish: har doim — , 0), , {}) yoki , []).
7.4 Sanoqda akkumulyator o'rniga son qaytarish
const buyurtmalar = ["osh", "choy", "osh"];
const sanoq = buyurtmalar.reduce((n, taom) => {
n[taom] = (n[taom] ?? 0) + 1;
return n[taom];
}, {});
console.log(sanoq);TypeError: Cannot create property 'choy' on number '1'Tarjimasi: "1 soniga choy xususiyatini yaratib bo'lmaydi". return n[taom] obyektni emas, sonni qaytardi. Ikkinchi qadamda akkumulyator 1 bo'ldi va kod songa kalit yozmoqchi bo'ldi. Xato choy da chiqdi, chunki u ikkinchi element — birinchi qadam muvaffaqiyatli o'tgan edi.
Bu xabarni Node'da va saytdagi «Ishga tushir» tugmasida ko'rasiz: ikkalasi kodni qat'iy rejimda (strict mode) bajaradi. Bu — JavaScript'ning jim xatolarni ham xato deb ko'rsatadigan qattiqroq rejimi, uni Strict mode darsida o'rganamiz. Brauzerdagi oddiy <script> faylida (masalan vazifalar.js da) esa songa yozish jimgina yo'qoladi. Callback undefined qaytaradi va xato bir qadam keyin chiqadi: Cannot read properties of undefined (reading 'osh'). Ikkala holatda ham xato return qatorida emas — boshqa joyda ko'rinadi. Tuzatish: return n; — akkumulyatorning o'zi.
8. Mashqlar
1-mashq (oson): Oylik xarajatlar
Aziz oy davomida yozib borgan xarajatlarining yig'indisini reduce bilan hisoblang.
const xarajatlar = [120000, 45000, 300000, 18000];Yechim
const xarajatlar = [120000, 45000, 300000, 18000];
const jami = xarajatlar.reduce((summa, x) => summa + x, 0);
console.log(jami); // 48300008-qism yakuniy loyihasidagi sikl endi bitta ifoda.
2-mashq (o'rta): Eng qimmat taom
menyu dan eng qimmat taom obyektini reduce bilan toping. Boshlang'ich qiymat — menyuning birinchi taomi.
Ishora: akkumulyator — "hozirgacha eng qimmati". Joriy taom qimmatroq bo'lsa — uni qaytaring, aks holda eskisini.
const menyu = [
{ nom: "Lag'mon", narx: 28000 },
{ nom: "Osh", narx: 35000 },
{ nom: "Manti", narx: 30000 },
];Yechim
const menyu = [
{ nom: "Lag'mon", narx: 28000 },
{ nom: "Osh", narx: 35000 },
{ nom: "Manti", narx: 30000 },
];
const engQimmat = menyu.reduce(
(eng, taom) => (taom.narx > eng.narx ? taom : eng),
menyu[0],
);
console.log(engQimmat.nom); // OshAkkumulyator — obyekt, lekin yangi yaratilmadi: callback mavjud obyektlardan birini tanlab qaytaradi. Hech narsa o'zgartirilmagani uchun menyu[0] ni boshlang'ich qiymat qilish xavfsiz. Bo'sh menyuda esa menyu[0] — undefined va eng.narx xato beradi. Real kodda avval menyu.length === 0 ni tekshiring.
3-mashq (qiyin): Vazifalar statistikasi
statistika(vazifalar) funksiyasini yozing. U bitta reduce bilan { hammasi, faol, bajarilgan } obyektini qaytarsin. So'ng xuddi shuni for...of bilan yozing va qaysi biri o'qishga osonroq ekanini o'ylab ko'ring.
Yechim
const vazifalar = [
{ id: 1, bajarildi: false },
{ id: 2, bajarildi: true },
{ id: 3, bajarildi: false },
];
function statistika(royxat) {
return royxat.reduce(
(s, v) => {
s.hammasi++;
if (v.bajarildi) {
s.bajarilgan++;
} else {
s.faol++;
}
return s;
},
{ hammasi: 0, faol: 0, bajarilgan: 0 },
);
}
console.log(statistika(vazifalar));
console.log(statistika([]));Konsolda:
{ hammasi: 3, faol: 2, bajarilgan: 1 }
{ hammasi: 0, faol: 0, bajarilgan: 0 }Boshlang'ich qiymat — uch nolli yangi obyekt. Shuning uchun bo'sh ro'yxat ham to'g'ri javob beradi. for...of varianti — const s = { ... }, sikl ichida xuddi shu if, oxirida return s. U ham to'g'ri va ko'pchilik uni tezroq o'qiydi. Bu — «Qachon reduce emas» bo'limidagi chegara: callback ichida if/else paydo bo'ldi. Ikkalasi ham qabul; jamoada qaysi uslub kelishilgan bo'lsa — shu.
4-mashq: Vazifalar qadami — oxirgi sikl va branch'ni yakunlash
refactor/hof-metodlar branch'idagi oxirgi ish. faollarSoni hali ham for...of bilan:
function faollarSoni() {
let soni = 0;
for (const vazifa of holat.vazifalar) {
if (!vazifa.bajarildi) {
soni++;
}
}
return soni;
}Uni reduce bilan yozing. Xulq o'zgarmasin. So'ng refaktoring natijasini sinab, branch'ni main ga qo'shing.
Yechim
function faollarSoni() {
return holat.vazifalar.reduce(
(soni, vazifa) => (vazifa.bajarildi ? soni : soni + 1),
0,
);
}Akkumulyator — "hozirgacha sanalgan faollar". Bajarilgan vazifada u o'zgarmay o'tadi, faolda bittaga oshadi. Boshlang'ich 0 — vazifa umuman bo'lmasa ham "0 ta vazifa qoldi" chiqadi.
Eski va yangi funksiya bir xil javob berishini Node'da tekshiring — har xil holatlarda, shu jumladan bo'sh ro'yxatda:
function eskisi(vazifalar) {
let soni = 0;
for (const vazifa of vazifalar) {
if (!vazifa.bajarildi) {
soni++;
}
}
return soni;
}
function yangisi(vazifalar) {
return vazifalar.reduce(
(soni, vazifa) => (vazifa.bajarildi ? soni : soni + 1),
0,
);
}
const holatlar = [
[],
[{ bajarildi: true }],
[{ bajarildi: false }, { bajarildi: true }, { bajarildi: false }],
];
for (const royxat of holatlar) {
console.log(eskisi(royxat), yangisi(royxat));
}Konsolda:
0 0
0 0
2 2Har qatorda ikki son teng — xulq o'zgarmadi.
filter bilan ham yozsa bo'lardi: holat.vazifalar.filter((v) => !v.bajarildi).length. U ham to'g'ri va qisqa, faqat sanash uchun vaqtinchalik massiv yasaydi. Ikkalasi ham qabul — biz bu darsda reduce ni tanladik.
Brauzerda butun ilovani sinab chiqing — bu to'rt darsdagi refaktoringning yakuniy tekshiruvi:
- Ochilganda — uch vazifa, "2 ta vazifa qoldi".
- Vazifani belgilang — "1 ta", qaytaring — "2 ta". Yangisini qo'shing — "3 ta".
- Hammasini o'chiring — "Hozircha vazifa yo'q. Birinchisini qo'shing!" va "0 ta vazifa qoldi".
- «Faol», «Bajarilgan», tahrirlash,
Esc, fokus — hammasi 09-qismdagidek.
vazifalar.js da endi bitta ham for...of yo'q:
| Funksiya | Edi | Endi | Dars |
|---|---|---|---|
render (tugmalar) |
for...of |
forEach |
Callback imzosi |
render (qatorlar) |
for...of + fragment |
map |
map |
korinadiganlar |
for...of + push |
filter |
filter |
faollarSoni |
for...of + let |
reduce |
shu dars |
Commit va branch'ni yakunlash:
git add assets/js/vazifalar.js
git commit -m "Render: faol vazifalarni reduce bilan sana"
git switch main
git merge refactor/hof-metodlar
git branch -d refactor/hof-metodlar
git pushPages "Deploy from a branch" rejimida — main ga push bo'lishi bilan sayt bir-ikki daqiqada yangilanadi (Yakuniy loyiha). Telefonda oching: foydalanuvchi hech qanday farq ko'rmasligi kerak. Refaktoringning muvaffaqiyati aynan shu.
9. Real ishda
- Savat va hisob-kitob. Internet do'kondagi "Jami: 245 000 so'm", Click/Payme to'lov summasi — ichida
reduce(yoki xuddi shu qolipdagi sikl). Pul bilan ishlaganda sonlarni so'mda, butun holda saqlang (Pul va aniq hisob-kitob). - Statistika. Admin paneldagi "bugungi buyurtmalar: 47, tushum: 3 450 000" — bir yoki bir necha
reduce. - Ma'lumotni qayta tuzish. Serverdan kelgan ro'yxatni
idbo'yicha lug'atga aylantirish — Redux kabi holat kutubxonalarida (kursda keyinroq) asosiy usul. - Jadval dasturlari. Excel va Google Sheets'dagi
SUM,COUNTIF,MAXformulalari — mohiyatanreduce: ustunni bitta songa yig'adi. Hisobotni dasturda qayta yozish kerak bo'lsa, har formula bittareducega aylanadi,COUNTIFesa —filterva uzunlik. - Intervyu. "
reduceyordamidamapni yozing", "boshlang'ich qiymat nega kerak?", "[].reduce((a, b) => a + b)nima beradi?" — uchalasi ham ko'p beriladi. Oxirgisining javobi:TypeError.
Xulosa
reducemassivni bitta qiymatga yig'adi: callback(akkumulyator, element)dan yangi akkumulyator qaytaradi.- Oxirgi qaytgan akkumulyator — natija. Natija turi istalgancha: son, satr, obyekt.
- Boshlang'ich qiymatni doim yozing: bo'sh massivda
TypeError, obyektlarda[object Object]xavfi yo'qoladi. - Jingalak qavsli callback'da
return akkumulyatorni unutmang. Birinchi parametr — doim akkumulyator, nomi ham shunday bo'lsin (jami,natija). reduceichida yaratilgan akkumulyatorni o'zgartirish mumkin; asl massivni — yo'q.- Aylantirish —
map, tanlash —filter, yig'ish —reduce. Murakkab yig'ish —for...ofham yaxshi tanlov.
Keyingi dars: sort va taqqoslash funksiyasi — nega [10, 9, 1].sort() sonlarni noto'g'ri tartiblashini va callback'ning yangi imzosi — (a, b) — bilan to'g'ri saralashni o'rganamiz.
Manbalar
- MDN: "Array.prototype.reduce()", "Array.prototype.reduceRight()" — developer.mozilla.org
- ECMAScript 2025 Language Specification: "Array.prototype.reduce" — tc39.es/ecma262
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!