2. blokk · Építőkövek · 6. fejezet
Eddig egy változó egy értéket tárolt. De mi van, ha egy webshop száz termékét kell kezelned? A tömb több értéket tárol sorba rendezve, egyetlen változóban: indexszel éred el az elemeket, ciklussal járod be, metódusokkal bővíted és szűkíted. Ebben a fejezetben a tömb alapműveleteit tanulod meg, és függvényeket írsz, amelyek tömböt kapnak és tömböt vagy számot adnak vissza.
A tömb (array) több értéket tárol sorba rendezve, egyetlen változóban. Szögletes zárójellel hozzuk létre, az elemeket vesszővel választjuk el. Egy tömbben bármilyen típus lehet, de a gyakorlatban egyféle „dolgot" tartunk benne:
const termekek = ["Egér", "Monitor", "Kábel"]; const arak = [6990, 45990, 1990]; const ures = []; // üres tömb — később töltjük fel console.log(termekek); // [ 'Egér', 'Monitor', 'Kábel' ] console.log(termekek.length); // 3 — az elemek száma
Az elemeket indexszel érjük el, amely 0-tól indul. Az index bármilyen kifejezés lehet — ezért az utolsó
elem a length - 1 helyen van:
console.log(termekek[0]); // Egér (az első) console.log(termekek[2]); // Kábel (a harmadik) console.log(termekek[termekek.length - 1]); // Kábel (az utolsó — mindig működik) console.log(termekek.at(-1)); // Kábel (ugyanaz, rövidebben; at(-2) az utolsó előtti) console.log(termekek[3]); // undefined — nincs ilyen index, de NEM hiba! termekek[1] = "Monitor 27\""; // elem módosítása index szerint
Az első elem indexe 0, egy 3 elemű tömb utolsó eleme a [2]. A [3]
már túl van a végén — nem hibát kapsz, hanem undefined-ot, ami később, egy számításban okoz
NaN-t. A ciklusfeltétel ezért mindig i < t.length, nem <=.
Négy metódus a tömb két végének kezelésére — mind a négy helyben módosítja a tömböt:
| Metódus | Mit csinál | Mit ad vissza |
|---|---|---|
push(elem) | a végére tesz egy (vagy több) elemet | az új hosszt |
pop() | az utolsót eltávolítja | az eltávolított elemet |
unshift(elem) | az elejére tesz egy elemet | az új hosszt |
shift() | az elsőt eltávolítja | az eltávolított elemet |
const kosar = ["Egér"]; kosar.push("Monitor"); // [ 'Egér', 'Monitor' ] kosar.push("Kábel", "Laptop"); // [ 'Egér', 'Monitor', 'Kábel', 'Laptop' ] kosar.unshift("Táska"); // [ 'Táska', 'Egér', 'Monitor', 'Kábel', 'Laptop' ] let utolso = kosar.pop(); // utolso = 'Laptop', a tömb 4 elemű let elso = kosar.shift(); // elso = 'Táska', a tömb 3 elemű console.log(kosar); // [ 'Egér', 'Monitor', 'Kábel' ]
A push a leggyakoribb: így építünk fel egy új tömböt ciklusban — üresről indulunk, és a megfelelő
elemeket beletesszük. Ezt a mintát a feladatok fele használja.
const arak = [6990, 45990, 1990]; console.log(arak.indexOf(1990)); // 2 — az első előfordulás indexe console.log(arak.indexOf(100)); // -1 — nincs benne (ezt így jelzi!) console.log(arak.includes(45990)); // true — csak azt mondja, benne van-e
Ha csak az érdekel, benne van-e, az includes olvashatóbb; ha az is, hogy hol, akkor
indexOf. Mindkettő szigorú (===) egyenlőséggel keres.
A slice(kezdo, veg) új tömböt ad a kezdo indextől a veg
indexig (a veg már nincs benne). Az eredeti tömb változatlan marad:
const t = [10, 20, 30, 40, 50]; console.log(t.slice(1, 3)); // [ 20, 30 ] — az 1. és 2. index console.log(t.slice(2)); // [ 30, 40, 50 ] — a 2. indextől a végéig console.log(t.slice(-2)); // [ 40, 50 ] — az utolsó 2 console.log(t.slice()); // [ 10, 20, 30, 40, 50 ] — teljes MÁSOLAT console.log(t); // változatlan
A splice(index, hany, ...ujak) az index-től hany elemet
kivesz az eredeti tömbből, és a helyükre beszúrja az esetleges további argumentumokat. A kivett elemeket adja vissza:
const t = [10, 20, 30, 40, 50]; const kivett = t.splice(1, 2); // az 1. indextől 2 elemet töröl console.log(kivett); // [ 20, 30 ] console.log(t); // [ 10, 40, 50 ] — az eredeti MEGVÁLTOZOTT t.splice(1, 0, 99); // 0 törlés + beszúrás az 1. indexre console.log(t); // [ 10, 99, 40, 50 ] t.splice(2, 1); // egy elem törlése index szerint (a 40) console.log(t); // [ 10, 99, 50 ]
Egy betű a különbség, de az egyik másol (slice, „szelet"), a másik kivág az eredetiből
(splice, „összefércel"). Ha a függvényednek új tömböt kell visszaadnia és az eredetit nem bánthatja,
slice-t vagy push-os felépítést használj.
const nevek = ["Egér", "Monitor", "Kábel"]; console.log(nevek.join(", ")); // Egér, Monitor, Kábel console.log(nevek.join("")); // EgérMonitorKábel console.log([2, 0, 2, 5].join("-")); // 2-0-2-5
Mindkettő helyben rendezi át a tömböt (és a tömböt magát adja vissza). A reverse() megfordítja a
sorrendet; a sort() rendez — de van egy híres csapdája:
const nevek = ["körte", "alma", "szilva"]; nevek.sort(); console.log(nevek); // [ 'alma', 'körte', 'szilva' ] — szövegnél jó const szamok = [100, 25, 3, 40]; szamok.sort(); console.log(szamok); // [ 100, 25, 3, 40 ] ← ROSSZ! "1" < "2" < "3" < "4" — szövegként hasonlított
Argumentum nélkül a sort minden elemet szöveggé alakít, és „ábécé" szerint rendez — így a 100 a
25 elé kerül, mert az "1" kisebb, mint a "2". Számokhoz
összehasonlító függvényt (comparator) kell adni.
A comparator egy callback (5. fejezet!), amely két elemet kap, és negatív számot ad, ha az első kerüljön előbbre, pozitívat, ha a második. Számoknál ez egyszerűen a kettő különbsége:
szamok.sort((a, b) => a - b); // növekvő: [ 3, 25, 40, 100 ] szamok.sort((a, b) => b - a); // csökkenő: [ 100, 40, 25, 3 ] const masolatRendezve = arak.slice().sort((a, b) => a - b); // az eredeti arak marad
A comparatorral (pl. objektumok rendezése ár szerint) a 10. fejezetben foglalkozunk részletesen — most elég, ha a
(a, b) => a - b mintát megjegyzed, és tudod, hogy számoknál kötelező.
A tömb elemein kétféleképpen megyünk végig. A for indexszel akkor kell, ha az indexre is szükség van
(pozíció, szomszédos elem, visszafelé haladás); a for…of egyszerűbb, ha csak az elemek érdekelnek:
const arak = [6990, 45990, 1990]; for (let i = 0; i < arak.length; i++) { console.log(i + ". ár: " + arak[i]); } // 0. ár: 6990 // 1. ár: 45990 // 2. ár: 1990
const arak = [6990, 45990, 1990]; for (const ar of arak) { console.log(ar); } // 6990 // 45990 // 1990
A 4. fejezet három alapmintája (számlálás, összegzés, keresés) tömbön ugyanígy működik — csak most függvénybe csomagolva, amely a tömböt paraméterként kapja és az eredményt visszaadja:
function osszeg(t) { // ÖSSZEGZÉS let s = 0; for (const x of t) { s += x; } return s; } function olcsokSzama(t, hatar) { // SZÁMLÁLÁS feltétellel let db = 0; for (const ar of t) { if (ar < hatar) db++; } return db; } function legnagyobb(t) { // KERESÉS: a legjobb eddig let max = t[0]; // az első elemről indulunk for (let i = 1; i < t.length; i++) { if (t[i] > max) max = t[i]; } return max; } function parosak(t) { // SZŰRÉS: új tömb felépítése push-sal const uj = []; for (const x of t) { if (x % 2 === 0) uj.push(x); } return uj; } console.log(osszeg([6990, 45990, 1990])); // 54970 console.log(olcsokSzama([6990, 45990, 1990], 10000)); // 2 console.log(parosak([1, 2, 3, 4])); // [ 2, 4 ]
Összegzés (gyűjtő 0-ról), számlálás (db++), legjobb-eddig (indulás az első elemről, csere ha jobb),
új tömb felépítése (üres tömb + feltételes push). Ha egy feladatnál elakadsz, kérdezd meg:
melyik a négy közül? A 10. fejezetben ezekre kapsz kész metódusokat (reduce, filter,
map) — de csak akkor fogod érteni őket, ha most kézzel megírtad.
Az Express-kurzus első CRUD API-jában a terméklistát memóriában, egy tömbben tároljuk: a GET /termekek
az egész tömböt küldi vissza, a POST push-ol bele, a DELETE /termekek/:id
megkeresi az indexet és splice-szal kiveszi. Minden, amit itt kézzel megírsz, ott egy-egy útvonalkezelő törzse lesz.
Egy szám vagy szöveg érték szerint kerül a változóba: let b = a után két független példány van. A tömb
referencia: a változó nem a tömböt, hanem egy „címkét" tárol, amely a memóriában lévő tömbre mutat. Két változó tehát
ugyanarra a tömbre mutathat:
const a = [1, 2, 3]; const b = a; // NEM másolat: b ugyanarra a tömbre mutat b.push(4); console.log(a); // [ 1, 2, 3, 4 ] — a is "megváltozott", mert ugyanaz a tömb console.log(a === b); // true const c = a.slice(); // valódi másolat: új tömb ugyanazokkal az elemekkel c.push(5); console.log(a); // [ 1, 2, 3, 4 ] — érintetlen console.log(c); // [ 1, 2, 3, 4, 5 ] console.log(a === c); // false — két külön tömb, még ha a tartalmuk egyezne is
Igen. A const azt tiltja, hogy a változó másik tömbre mutasson (a = [9] hiba lenne),
de a tömb tartalma szabadon változhat: a.push(4), a[0] = 7 rendben van. Ezért írjuk a
tömböket általában const-tal.
Ugyanez történik függvénynek átadott tömbbel: a paraméter ugyanarra a tömbre mutat, így ha a függvény push-ol
vagy sort-ol rajta, az a hívó tömbjét is megváltoztatja. A feladatokban, ahol „új tömböt" kérünk, ezért építs
push-sal újat vagy indulj slice() másolatból — az eredeti maradjon érintetlen.
A tömb eleme lehet egy másik tömb — így kapunk tömbök tömbjét, azaz táblázatot (mátrixot). A külső tömb a sorokat, a belsők az oszlopokat tartalmazzák; egy elemet két indexszel érünk el:
const matrix = [ [1, 2, 3], // 0. sor [4, 5, 6], // 1. sor [7, 8, 9] // 2. sor ]; console.log(matrix[1]); // [ 4, 5, 6 ] — az 1. sor (maga is tömb) console.log(matrix[1][2]); // 6 — 1. sor, 2. oszlop console.log(matrix.length); // 3 sor console.log(matrix[0].length); // 3 oszlop az első sorban
A bejárás két egymásba ágyazott ciklus (4. fejezet): a külső a sorokon, a belső egy sor elemein megy végig:
let osszeg = 0; for (let s = 0; s < matrix.length; s++) { // s: sor index let sorSzoveg = ""; for (let o = 0; o < matrix[s].length; o++) { // o: oszlop index az adott sorban sorSzoveg += matrix[s][o] + " "; osszeg += matrix[s][o]; } console.log(sorSzoveg); // 1 2 3 / 4 5 6 / 7 8 9 } console.log(osszeg); // 45 // for…of-fal ugyanez, ha nem kell az index: for (const sor of matrix) { for (const elem of sor) { /* … */ } }
Kétdimenziós tömb a webshopban: egy rendelés tételei soronként [ár, darab] párokként; egy heti eladási
táblázat, ahol minden sor egy nap. A 7. fejezettől inkább objektumok tömbjét használjuk erre — de a két ciklus ugyanaz marad.
A typeof egy tömbre "object"-et mond — ezért tömböt az Array.isArray-jel
ellenőrzünk:
console.log(typeof [1, 2]); // object — nem segít console.log(Array.isArray([1, 2])); // true console.log(Array.isArray("abc")); // false
Az üres tömb a leggyakoribb szélső eset, és a feladatokban mindig szerepel egy ilyen teszt. Gondold végig, mit adjon a
függvényed, ha nincs elem: az összeg természetesen 0, de az átlag 0 / 0 = NaN lenne, a „legnagyobb" pedig
t[0] = undefined. Ezeket egy guard clause-zal (5. fejezet) intézd el a függvény elején:
function atlag(t) { if (t.length === 0) { return 0; // üres tömb: megállapodás szerint 0 } let s = 0; for (const x of t) s += x; return s / t.length; } console.log(atlag([])); // 0 (NaN helyett) console.log([].length === 0); // true — így ellenőrizd az ürességet (a [] maga truthy!)
if (t) egy üres tömbre is igaz, mert minden objektum truthy. Az ürességet mindig
t.length === 0-val nézd.
Kísérletezz a tömbökkel: próbáld ki, mi módosítja az eredetit és mi ad újat, nézd meg a sort buktatóját,
és járj be egy mátrixot. 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) ↓
Mint az előző fejezetben: egy megadott nevű függvényt írsz, az ellenőrző több argumentummal meghívja, és a
visszaadott értéket hasonlítja a várthoz — tömböknél elemenként, sorrendben. A függvények tömböt kapnak paraméterként;
csak ciklusokat és az itt tanult metódusokat használd (a map/filter/reduce
még nem jött — ahol a leírás tiltja a reverse-t vagy az includes-t, ott a saját
ciklusod a lényeg). Ahol „új tömböt" kérünk, az eredeti nem módosulhat. Minden feladatnál van üres vagy egyelemű tömbös teszt is!
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.
Számot kell visszaadni → összegzés, számlálás vagy legjobb-eddig. Logikai értéket → keresés early return-nel. Tömböt →
üres tömb + push (vagy slice másolat). Mátrixot → két ciklus. Előbb döntsd el,
melyik, és csak aztán gépelj.
[a, b, c]. Index 0-tól, elemszám length, utolsó elem t[t.length - 1] vagy t.at(-1). Nem létező index → undefined.push/pop (vége), unshift/shift (eleje) — helyben módosítanak. Új tömböt [] + push-sal építünk.indexOf (index vagy −1), includes (true/false). Kivágás: slice másol (nem módosít), splice helyben töröl/beszúr. join szöveget ad.reverse() és sort() helyben rendez át. A sort() alapból szöveg szerint — számokhoz sort((a, b) => a - b).for (let i = 0; i < t.length; i++) ha kell az index, for (const x of t) ha csak az elem. Minták: összegzés, számlálás, legjobb-eddig, új tömb felépítése.b = a nem másol, ugyanarra mutat; a.slice() valódi másolat. A const tömb tartalma módosítható, csak a változó nem mutathat másra.m[sor][oszlop], bejárás két ciklussal. Array.isArray(x) mondja meg, tömb-e; az ürességet t.length === 0 jelzi — kezeld guard clause-zal.A tömb sorszámozott lista. A következő fejezetben az objektumokkal egy dolog nevesített tulajdonságait
fogjuk össze: { nev: "Egér", ar: 6990, keszlet: 40 }. A kettő együtt — objektumok tömbje — a
webshop terméklistája, és minden Node-os API adatformátuma.