Vezérlőpult
10. Tömb-metódusok és adatfeldolgozás · Modern JS

3. blokk · Modern JS · 10. fejezet

Tömb-metódusok és adatfeldolgozás

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.

Élő gyakorló13 feladat automatikus ellenőrzéssel≈ 1,5 óra

Tanulási célok

1A ciklus bele van építve

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:

Ciklussal
const nevek = ["Egér", "Monitor", "Kábel"];
for (let i = 0; i < nevek.length; i++) {
  console.log(i + ". " + nevek[i]);
}
forEach-csel
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ódusA callback mit ad visszaA 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/hamisvan-e legalább egy / mind igaz-e
reduce(f, kezdo)az új gyűjtőértéketegyetlen érték (szám, objektum, bármi)
sort(f)negatív / 0 / pozitívugyanaz a tömb, helyben rendezve

2map és filter

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)
⚠️ OBJEKTUMOT VISSZAADÓ ARROW: ZÁRÓJELBE!

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()));
✅ map = ÁTALAKÍT, filter = VÁLOGAT

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).

3find, findIndex, some, every

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.

4reduce — a mindenes

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:

Összegzés ciklussal
let osszeg = 0;              // kezdőérték
for (const ar of arak) {
  osszeg = osszeg + ar;      // frissítés
}
console.log(osszeg);
Összegzés reduce-szal
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örakk (eddigi)ar (elem)visszaadott = új akk
1.0 (kezdőérték)69906990
2.69904599052980
3.52980899061970
a reduce eredménye61970

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 }
⛔ KÉT KLASSZIKUS reduce-HIBA

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, {}, []).

5sort comparatorral

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 HELYBEN RENDEZ — ELŐBB MÁSOLJ!

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.)

6Láncolás

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:

Ciklussal (4–6. fejezet)
const nevek = [];
for (const t of termekek) {
  if (t.keszlet > 0 && t.ar < 10000) {
    nevek.push(t.nev.toUpperCase());
  }
}
const szoveg = nevek.join(", ");
Metódusokkal láncolva
const szoveg = termekek
  .filter(t => t.keszlet > 0 && t.ar < 10000)
  .map(t => t.nev.toUpperCase())
  .join(", ");


⚙️ HOL JÖN ELŐ NODE-BAN?

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.

7flat, Array.from — és mikor ne használd

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".

Mikor maradj a ciklusnál?

8Élő kód — próbáld ki!

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á.

JavaScript-gyakorló

Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓

A kimenet itt jelenik meg…

9Feladatok

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!

Gyakorló feladatok

⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.

Kész: 0 / 0
✅ MELYIK METÓDUS KELL?

Kérdezd meg magadtól, mit vársz eredményül. Ugyanannyi elem, más alakbanmap. Kevesebb elemfilter. Egy elemfind. Igen/nemsome/every. Egy szám vagy egy objektum az egészbőlreduce. Más sorrend[...t].sort(…).

10Összegzés

  1. A tömb-metódusok callbacket kapnak: a ciklus be van építve, te az egy elemre vonatkozó műveletet írod le. A callback első paramétere az elem, a második az index.
  2. forEach csak végrehajt; map átalakít (új tömb, ugyanannyi elem); filter válogat (új tömb, kevesebb elem).
  3. find az első találat (undefined, ha nincs), findIndex az indexe (-1); some = van-e ilyen, every = mind ilyen-e (üres tömbre true!).
  4. 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.
  5. 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(…).
  6. Láncolás: filter → sort → map → join, filter → reduce — soronként egy lépés, segédváltozó nélkül.
  7. 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.
➡️ KÖVETKEZŐ LÉPÉS

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.