3. blokk · Modern JS · 10. fejezet
A 4. fejezet három alapmintája — számlálás, összegzés, keresés — annyira gyakori, hogy a JavaScript beépítette
őket a tömbökbe. A map, filter, reduce,
find, some, every és sort
metódusokkal ciklus írása nélkül alakítod, szűröd, összesíted és rendezed az adatokat — és láncolva egy
lekérdezés teljes feldolgozását néhány sorban leírod. Egy Express API belseje pontosan így néz ki.
Az 5. fejezetben láttad, hogy egy függvény átadható másik függvénynek (callback). A tömb-metódusok erre épülnek:
a metódus végigmegy az elemeken, és minden elemre meghívja a te függvényedet. A ciklus tehát megvan —
te csak azt írod le, mi történjen egy elemmel. A legegyszerűbb ilyen a forEach:
const nevek = ["Egér", "Monitor", "Kábel"]; for (let i = 0; i < nevek.length; i++) { console.log(i + ". " + nevek[i]); }
const nevek = ["Egér", "Monitor", "Kábel"]; nevek.forEach((nev, i) => { console.log(i + ". " + nev); });
A callback első paramétere az aktuális elem, a második (opcionális) az indexe. Ez minden alábbi metódusnál így van.
A forEach nem ad vissza semmit — csak „csinál valamit" minden elemmel. Ha az eredmény is kell, a többi metódus jön.
| Metódus | A callback mit ad vissza | A metódus mit ad vissza |
|---|---|---|
forEach(f) | — | undefined |
map(f) | az elem átalakított változatát | új tömb, ugyanannyi elem |
filter(f) | igaz/hamis: maradjon-e | új tömb a megfelelő elemekből |
find(f) / findIndex(f) | igaz/hamis: ez az? | az első találat / indexe (undefined / -1) |
some(f) / every(f) | igaz/hamis | van-e legalább egy / mind igaz-e |
reduce(f, kezdo) | az új gyűjtőértéket | egyetlen érték (szám, objektum, bármi) |
sort(f) | negatív / 0 / pozitív | ugyanaz a tömb, helyben rendezve |
A map minden elemből készít valamit, és az eredményeket egy új tömbbe teszi —
ugyanannyi elem lesz, mint az eredetiben. Objektumok tömbjéből tipikusan egy mezőt szedünk ki, vagy új alakra hozzuk:
const arak = [6990, 45990, 8990]; const brutto = arak.map(ar => Math.round(ar * 1.27)); console.log(brutto); // [ 8877, 58407, 11417 ] console.log(arak); // [ 6990, 45990, 8990 ] — az eredeti érintetlen const termekek = [ { nev: "Egér", ar: 6990, keszlet: 3 }, { nev: "Monitor", ar: 45990, keszlet: 0 }, { nev: "Kábel", ar: 1990, keszlet: 12 } ]; const nevek = termekek.map(t => t.nev); // [ "Egér", "Monitor", "Kábel" ] const cimkek = termekek.map(t => `${t.nev} – ${t.ar} Ft`); const roviditett = termekek.map(({ nev, ar }) => ({ nev, ar })); // csak két mező (9. fejezet)
t => { nev: t.nev } nem objektumot ad vissza — a kapcsos zárójelet a JavaScript függvénytörzsnek nézi.
Tedd zárójelbe: t => ({ nev: t.nev }).
A filter egy feltétel alapján válogat: a callback igaz/hamis értéket ad, és csak az igazak maradnak.
Az eredmény új tömb, 0 és „mind" közötti elemszámmal, az eredeti sorrendben:
const raktaron = termekek.filter(t => t.keszlet > 0); console.log(raktaron.length); // 2 — Egér és Kábel const olcso = termekek.filter(t => t.ar < 5000); // [ { nev: "Kábel", … } ] const nincs = termekek.filter(t => t.ar > 100000); // [] — üres tömb, NEM undefined // keresés szövegben, kis/nagybetűtől függetlenül (8. fejezet) const kereses = "egér"; const talalat = termekek.filter(t => t.nev.toLowerCase().includes(kereses.toLowerCase()));
Ha az elemek száma változik → filter. Ha az elemek alakja változik → map.
Ha mindkettő → először filter, aztán map (kevesebb elemet kell átalakítani).
A kereső minta négy változata. A find az első megfelelő elemet adja (magát az elemet),
a findIndex az indexét. Ha nincs találat: undefined, illetve -1 —
erre mindig gondolj, mielőtt a találat mezőit olvasnád:
const kabel = termekek.find(t => t.nev === "Kábel"); console.log(kabel.ar); // 1990 const nincs = termekek.find(t => t.nev === "Webkamera"); console.log(nincs); // undefined console.log(nincs?.ar ?? "nincs ilyen"); // "nincs ilyen" — a 9. fejezet párosa jól jön const hol = termekek.findIndex(t => t.ar > 10000); console.log(hol); // 1
A some azt kérdezi: van-e legalább egy ilyen? Az every: mind ilyen?
Mindkettő logikai értéket ad, és korán megáll (a some az első igaznál, az every az első hamisnál):
console.log(termekek.some(t => t.keszlet === 0)); // true — a Monitor kifogyott console.log(termekek.every(t => t.ar > 1000)); // true — mind drágább 1000-nél console.log(termekek.every(t => t.keszlet > 0)); // false console.log([].some(t => true)); // false — üres tömbben nincs „egy sem" console.log([].every(t => false)); // true — üres tömbre az every MINDIG igaz (nincs ellenpélda)
Az egyszerű „benne van-e ez az érték?" kérdésre továbbra is az includes(x) és az indexOf(x)
való (6. fejezet) — a some/find akkor kell, ha feltétel alapján keresel,
pl. objektumok egy mezője szerint.
A reduce egy tömböt egyetlen értékké „zsugorít". Ez a 4. fejezet gyűjtő-mintája
(gyűjtő a ciklus előtt → frissítés minden körben → eredmény a végén) beépítve. A callback két paramétert kap:
az akkumulátort (a gyűjtő eddigi értéke) és az aktuális elemet; azt adja vissza, ami a gyűjtő új értéke lesz.
A második argumentum a kezdőérték:
let osszeg = 0; // kezdőérték for (const ar of arak) { osszeg = osszeg + ar; // frissítés } console.log(osszeg);
const osszeg = arak.reduce( (akk, ar) => akk + ar, // frissítés: az új gyűjtő 0 // kezdőérték ); console.log(osszeg);
Lépésenként, arak = [6990, 45990, 8990] esetén:
| Kör | akk (eddigi) | ar (elem) | visszaadott = új akk |
|---|---|---|---|
| 1. | 0 (kezdőérték) | 6990 | 6990 |
| 2. | 6990 | 45990 | 52980 |
| 3. | 52980 | 8990 | 61970 |
a reduce eredménye | 61970 | ||
Az akkumulátor bármi lehet — nem csak szám. Ez teszi a reduce-t mindenessé:
// maximum: a gyűjtő az eddigi legnagyobb const max = arak.reduce((akk, ar) => ar > akk ? ar : akk, arak[0]); // összérték: ár × készlet minden termékre const raktarErtek = termekek.reduce((akk, t) => akk + t.ar * t.keszlet, 0); // számlálás: hány van raktáron const db = termekek.reduce((akk, t) => t.keszlet > 0 ? akk + 1 : akk, 0); // csoportosítás objektumba: { kategória: darabszám } const lista = [ { nev: "Egér", kategoria: "periféria" }, { nev: "Monitor", kategoria: "kijelző" }, { nev: "Billentyűzet", kategoria: "periféria" } ]; const kategoriak = lista.reduce((akk, t) => { akk[t.kategoria] = (akk[t.kategoria] ?? 0) + 1; // ha még nincs ilyen kulcs, 0-ról indul return akk; // a gyűjtőt kell visszaadni! }, {}); // kezdőérték: üres objektum console.log(kategoriak); // { periféria: 2, kijelző: 1 }
1) Elfelejtett return a többsoros callbackben → a következő körben az akkumulátor undefined.
2) Hiányzó kezdőérték: ilyenkor az első elem lesz a kezdő akkumulátor — üres tömbnél ez hibát dob, objektumok tömbjénél
pedig objektumhoz próbálsz számot adni. Mindig adj kezdőértéket (0, {}, []).
A 6. fejezetben láttad: a sort() paraméter nélkül szövegként rendez — a [10, 9, 100]-ból
[10, 100, 9] lesz. A megoldás egy comparator callback, amely két elemet kap, és egy számot ad vissza:
negatív, ha a előbbre való, pozitív, ha b, 0, ha egyenlők:
const szamok = [10, 9, 100, 1]; szamok.sort((a, b) => a - b); // [ 1, 9, 10, 100 ] növekvő szamok.sort((a, b) => b - a); // [ 100, 10, 9, 1 ] csökkenő // objektumok egy mező szerint termekek.sort((a, b) => a.ar - b.ar); // ár szerint növekvő termekek.sort((a, b) => b.keszlet - a.keszlet); // készlet szerint csökkenő // szövegek: localeCompare (8. fejezet) — magyar ékezetekkel is jó const nevek = ["Zoli", "Ádám", "Béla"]; nevek.sort((a, b) => a.localeCompare(b, "hu")); // [ "Ádám", "Béla", "Zoli" ] termekek.sort((a, b) => a.nev.localeCompare(b.nev, "hu"));
A sort a map/filter-től eltérően az eredeti tömböt
rendezi át (és azt is adja vissza). Ha az eredeti sorrend még kell — egy szerveren a kapott adatot szinte soha nem szabad átírni —,
előbb készíts másolatot a 9. fejezet spread-jével: const rendezett = [...termekek].sort((a, b) => a.ar - b.ar);.
(Ugyanígy helyben módosít a reverse, a push és a splice is.)
Mivel a filter, a map és a (másolaton hívott) sort mind tömböt ad vissza,
a következő metódust rögtön rá lehet hívni. Így egy teljes adatfeldolgozó lépéssor olvasható „mondat" lesz — soronként egy lépés:
const lista = termekek .filter(t => t.keszlet > 0) // 1) csak ami raktáron van .sort((a, b) => a.ar - b.ar) // 2) ár szerint (a filter már új tömböt adott → biztonságos) .map(t => `${t.nev}: ${t.ar} Ft`) // 3) szöveggé alakítva .join("\n"); // 4) egy többsoros szöveg console.log(lista); // Kábel: 1990 Ft // Egér: 6990 Ft const raktarErtek = termekek .filter(t => t.keszlet > 0) .reduce((akk, t) => akk + t.ar * t.keszlet, 0); // filter → reduce: szűrt összeg const top3Nev = [...termekek] .sort((a, b) => b.ar - a.ar) .slice(0, 3) // a slice is tömböt ad → láncolható .map(t => t.nev);
Ugyanaz a feladat kétféleképp — figyeld meg, hogy a láncolt változatban nincs segédváltozó, nincs index, nincs push:
const nevek = []; for (const t of termekek) { if (t.keszlet > 0 && t.ar < 10000) { nevek.push(t.nev.toUpperCase()); } } const szoveg = nevek.join(", ");
const szoveg = termekek .filter(t => t.keszlet > 0 && t.ar < 10000) .map(t => t.nev.toUpperCase()) .join(", ");
Egy GET /termekek?kategoria=periféria&rendezes=ar kérés kiszolgálása szó szerint ez a lánc:
a lekérdezés szűrése filter, a rendezés sort (másolaton!), a válasz alakja map
(csak a nyilvános mezők), végül res.json(eredmeny). Egy GET /termekek/:id pedig
find + „ha undefined, 404". A rendelés összege reduce. Ez az öt metódus
az Express-kód gerince.
Két kisebb, de hasznos eszköz. A flat() az egymásba ágyazott tömböket egy szintre lapítja
(alapból egy szinttel; flat(Infinity) teljesen). Az Array.from({ length: n }, f) pedig
egy n elemű tömböt gyárt, minden indexhez az f által adott értékkel:
const matrix = [[1, 2], [3, 4], [5]]; console.log(matrix.flat()); // [ 1, 2, 3, 4, 5 ] const rendelesek = [{ tetelek: ["Egér", "Kábel"] }, { tetelek: ["Monitor"] }]; console.log(rendelesek.map(r => r.tetelek).flat()); // [ "Egér", "Kábel", "Monitor" ] console.log(Array.from({ length: 5 }, (_, i) => i + 1)); // [ 1, 2, 3, 4, 5 ] console.log(Array.from({ length: 3 }, () => 0)); // [ 0, 0, 0 ]
Az _ a callback első paramétere (az elem — itt undefined, nem használjuk); a megnevezés
konvenció: „ez a paraméter nem érdekel".
break) — a forEach/map-ból nem lehet kilépni. (Kivétel: ha csak az első találat kell, az find/some — azok megállnak.)a[i] és b[i]), vagy az index alapján számolsz ki valamit, ami nem az elemtől függ.reduce-os változat csak nehezebben olvasható lenne. A cél az olvashatóság, nem a ciklus száműzése.Kísérletezz a metódusokkal! Cseréld ki a feltételeket, adj hozzá egy lépést a lánchoz, próbáld ki, mi történik a reduce-szal
kezdőérték nélkül, vagy a sort-tal másolat nélkül. A kimenet úgy jelenik meg, ahogy Node-ban a console.log mutatná.
Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓
A webshop adatait dolgozod fel: a termékek { nev, ar, keszlet } (néhol kategoria) mezőkkel érkeznek.
Minden feladat egy-egy metódust vagy láncot gyakoroltat — igyekezz ciklus nélkül, a megfelelő metódussal megoldani. Az
Ellenőrzés a visszatérési értéket hasonlítja; figyelj az üres tömb esetére!
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.
Kérdezd meg magadtól, mit vársz eredményül. Ugyanannyi elem, más alakban → map.
Kevesebb elem → filter. Egy elem → find.
Igen/nem → some/every. Egy szám vagy egy objektum az egészből →
reduce. Más sorrend → [...t].sort(…).
forEach csak végrehajt; map átalakít (új tömb, ugyanannyi elem); filter válogat (új tömb, kevesebb elem).find az első találat (undefined, ha nincs), findIndex az indexe (-1); some = van-e ilyen, every = mind ilyen-e (üres tömbre true!).reduce((akk, elem) => újAkk, kezdő) egy értékké zsugorít: összeg, maximum, számlálás, csoportosító objektum. Mindig adj kezdőértéket, és többsoros callbackben ne felejtsd a return-t.sort((a, b) => a - b) számokra, a.localeCompare(b, "hu") szövegekre, a.ar - b.ar mező szerint. A sort helyben rendez → [...t].sort(…).filter → sort → map → join, filter → reduce — soronként egy lépés, segédváltozó nélkül.flat() lapít, Array.from({ length: n }, f) tömböt gyárt. Ciklus marad, ha break kell vagy több tömböt jársz párhuzamosan.Az adatok alakítását tudod — a következő fejezetben az adatok formátumairól és tárolóiról lesz szó: a JSON,
amiben egy API küld és fogad adatot (JSON.stringify/parse), a Map és Set
gyűjtemények, valamint a Date és a Math leggyakoribb műveletei.