Blok 2 · Ders 11
Dizi metotları: map, filter, find, reduce
Döngü yazmadan veri dönüştürmek: forEach, map, filter, find, some, every, reduce, sort ve bunları zincirlemek.
Bu dersin sonunda
forEachile bir dizinin her elemanına iş yaptırabilir,for...ofile farkını söyleyebilirsinmapile dönüştürüpfilterile süzerek döngü yazmadan yeni diziler üretebilirsinfind,findIndex,someveeveryile dizide arayıp bulunamama durumunu (undefined,-1) güvenle ele alabilirsinreduceile bir diziden toplam, en büyük değer ve gruplanmış bir obje çıkarabilirsinsort'a karşılaştırma fonksiyonu verip doğru sıralayabilir, asıl diziyi korumak içintoSortedkullanabilirsin- Metotları zincirleyip bir iş için hangi metodun gerektiğine karar verebilirsin
Bu sayfada
Ders 5'ten beri bir dizinin üstünden geçmek için hep döngü yazdın: boş bir dizi hazırla, sayacı kur, koşulu yaz, push et. Bu derste aynı işleri tek satırda yapan dizi metotlarını öğreneceksin: forEach, map, filter, find, some, every, reduce ve sort. Ders 10'da öğrendiğin callback asıl işini burada görüyor: döngüyü metot kurar, sen yalnızca her elemana ne yapılacağını söylersin.
Dersin sonunda bir takımın canlılarını süzüp altınlarını toplayan, skor tablosunu doğru sıralayan birkaç satırlık kodlar yazabilecek ve bir iş için hangi metodu seçeceğini bileceksin.
Aynı iskelet, üç kez
Elinde bir takımın bilgileri var ve üç şey öğrenmek istiyorsun: şampiyonların adları, hâlâ hayatta olanlar ve takımın toplam altını. Ders 5'te öğrendiğin gibi her soru için bir döngü yazarsın. C:\kod\ogren klasöründe yeni bir dosya aç: ders11.js.
const takim = [ { isim: "Ahri", hp: 100, gold: 300 }, { isim: "Garen", hp: 0, gold: 450 }, { isim: "Jinx", hp: 60, gold: 1200 }, { isim: "Lux", hp: 0, gold: 800 }] const adlar = []for (const sampiyon of takim) { adlar.push(sampiyon.isim)} const canlilar = []for (const sampiyon of takim) { if (sampiyon.hp > 0) { canlilar.push(sampiyon.isim) }} let toplamAltin = 0for (const sampiyon of takim) { toplamAltin = toplamAltin + sampiyon.gold} console.log(adlar)console.log(canlilar)console.log(toplamAltin)[ 'Ahri', 'Garen', 'Jinx', 'Lux' ][ 'Ahri', 'Jinx' ]2750Program doğru çalışıyor. Şimdi üç döngüyü karşılaştır: üçü de aynı iskeletten çıkmış. Önce sonucu koyacağın bir kutu hazırlıyorsun, sonra diziyi baştan sona geziyorsun, her elemanda küçük bir iş yapıyorsun. Değişen tek şey o küçük iş: adı al; canı 0'dan büyükse ekle; altını topla.
Programcılar bu iskeleti binlerce kez yazınca diller hazırını getirmeye başladı. Dizilerin push ve includes gibi metotları olduğunu Ders 5'te gördün; noktayla çağrılan başka metotları da var ve bunlar döngüyü kendi içlerinde kurar. Sen yalnızca "her elemanda ne yapılacak" kısmını bir fonksiyon olarak verirsin. Ders 10'daki adıyla: bir callback.
Dosyandaki üç döngüyü sil, yerlerine şu satırları yaz. Dizi aynı kalıyor:
const takim = [ { isim: "Ahri", hp: 100, gold: 300 }, { isim: "Garen", hp: 0, gold: 450 }, { isim: "Jinx", hp: 60, gold: 1200 }, { isim: "Lux", hp: 0, gold: 800 }] const adlar = takim.map((sampiyon) => sampiyon.isim)const canlilar = takim .filter((sampiyon) => sampiyon.hp > 0) .map((sampiyon) => sampiyon.isim)const toplamAltin = takim.reduce( (toplam, sampiyon) => toplam + sampiyon.gold, 0) console.log(adlar)console.log(canlilar)console.log(toplamAltin)[ 'Ahri', 'Garen', 'Jinx', 'Lux' ][ 'Ahri', 'Jinx' ]2750Çıktı birebir aynı, ama döngüler, boş diziler ve push'lar ortadan kalktı. Satırları şöyle okumaya alış: takim.map(...) "takımın her elemanını şuna dönüştür", takim.filter(...) "takımdan şu koşula uyanları seç", takim.reduce(...) "takımı tek bir değere indir". Bu derste hepsini tek tek açacağız. canlilar satırlarındaki gibi filter ile map'i art arda bağlamak da Ders 9'da metinlerde gördüğün zincirlemenin dizi hâli; onu dersin sonunda göreceksin.
forEach: her eleman için bir iş
En basit metotla başla. forEach, Türkçesiyle "her biri için": dizinin her elemanı için verdiğin fonksiyonu bir kez çağırır. Önce Ders 5'teki for...of hâli, sonra aynı iş forEach ile:
const adlar = ["Lux", "Vi", "Zed"]for (const ad of adlar) { console.log(`${ad} hazır`)}const adlar = ["Lux", "Vi", "Zed"]adlar.forEach((ad) => { console.log(`${ad} hazır`)})İkisi de aynı çıktıyı verir:
Lux hazırVi hazırZed hazırforEach'in parantezine bir ok fonksiyonu verdin. forEach dizinin başından başlar, her eleman için bu fonksiyonu çağırır ve elemanı fonksiyonun parametresine koyar: önce ad "Lux" olur, sonra "Vi", en son "Zed".
Fonksiyonu sen çağırmıyorsun, forEach çağırıyor; tıpkı Ders 10'daki herSampiyonIcin gibi. O dersteki herSampiyonIcin(takim, islem) çağrısını artık takim.forEach(islem) diye yazabilirsin. Ders 10'daki kaptan ve emir kartı benzetmesi burada da geçerli: forEach takımı sırayla dolaşan kaptan, verdiğin callback de kartta yazan emir.
Parametreye istediğin adı verebilirsin. İyi bir alışkanlık: dizinin adı çoğulsa parametreye onun tekilini ver: adlar için ad, fiyatlar için fiyat. Dizinin adı takim gibi bir topluluk adıysa parametreye tek bir üyenin adını ver: sampiyon. Kodu okuyan, parametrenin "dizinin tek bir elemanı" olduğunu adından anlar.
İkinci parametre: sıra numarası
forEach callback'e elemanın yanında sıra numarasını (index) da verir. Almak istersen ikinci bir parametre yaz. Bunu VS Code'da dene:
const adlar = ["Lux", "Vi", "Zed"] adlar.forEach((ad, sira) => { console.log(`${sira + 1}. ${ad}`)})PS C:\kod\ogren> node ders11.js1. Lux2. Vi3. ZedJavaScript parametrelere adlarına değil sırasına bakar: birinci parametreye eleman, ikinciye sıra numarası gelir. Sıra numarası Ders 5'teki gibi 0'dan başlar; ekranda 1'den başlasın diye sira + 1 yazdık.
Callback'e üçüncü bir şey daha gelir: dizinin kendisi. Çok seyrek gerekir, şimdilik varlığını bilmen yeter. Bu (eleman, sıra numarası, dizi) düzeni birazdan göreceğin map, filter, find, findIndex, some ve every için de aynıdır.
forEach geriye bir şey vermez
forEach her eleman için işini yapar ve biter; geriye bir değer döndürmez. Sonucunu bir kutuya koymaya kalkarsan undefined alırsın:
const adlar = ["Lux", "Vi", "Zed"]const sonuc = adlar.forEach((ad) => console.log(ad))console.log(sonuc)LuxViZedundefinedYani forEach ekrana yazmak, bir sayacı artırmak gibi yan etkiler içindir. Bir diziden yeni bir dizi üretmek istiyorsan doğru metot forEach değil, birazdan göreceğin map.
SyntaxError: Illegal break statementNe zaman çıkar? forEach'i yarıda kesmek için içine break yazınca. break yalnızca gerçek döngülerin (for, for...of, while) içinde çalışır; forEach'e verdiğin şey bir döngü değil, bir fonksiyon. Node bunu daha program başlamadan fark eder.
const adlar = ["Lux", "Vi", "Zed"]adlar.forEach((ad) => { if (ad === "Vi") { break } console.log(ad)})C:\kod\ogren\ders11.js:4 break ^^^^^ SyntaxError: Illegal break statement at wrapSafe (node:internal/modules/cjs/loader:1861:18) ... Node.js v24.20.0Nasıl düzelir? Yarıda durman gerekiyorsa for...of kullan; orada break çalışır. Aradığın şey "koşula uyan ilk elemanı bulmak"sa birazdan göreceğin find tam bu iş için var. forEach'in içinde return yazarsan forEach durmaz; yalnız o elemanın kalanı atlanır, tıpkı Ders 5'teki continue gibi.
for...of mu, forEach mi?
| for...of | forEach | |
|---|---|---|
| Ne yazarsın? | döngü | metot ve callback |
break ile yarıda kesmek | olur | olmaz (SyntaxError) |
| Sıra numarası | ayrı bir sayaç gerekir | callback'in ikinci parametresi |
| Geriye ne verir? | döngü değer vermez | undefined |
İkisi de aynı işi görür; hangisini seçeceğin çoğunlukla alışkanlık meselesi. Yarıda durman gerekebilecekse for...of, her elemana kısa bir iş yaptıracaksan forEach rahat okunur. Bu dersteki öteki metotlar ise forEach'ten farklı olarak geriye bir değer verir; asıl güçleri de oradan gelir.
map: her elemandan yeni bir değer
TFT'de yedek kulübendeki birimlerin fiyatları belli. Bir birimi 2 yıldıza çıkarmak için ondan 3 kopya gerekir. Her birimin 2 yıldızlı hâlinin kaça mal olacağını bir dizi olarak istiyorsun: her eleman yeni bir değere dönüşecek. Bu işin metodu map.
const fiyatlar = [4, 1, 5, 3]const ikiYildiz = []for (const fiyat of fiyatlar) { ikiYildiz.push(fiyat * 3)}console.log(ikiYildiz)const fiyatlar = [4, 1, 5, 3]const ikiYildiz = fiyatlar.map( (fiyat) => fiyat * 3)console.log(ikiYildiz)İkisinin çıktısı da aynı:
[ 12, 3, 15, 9 ]Kod kutusu dar olduğu için callback'i alt satıra aldık. Kendi dosyanda tek satırda yazabilirsin: const ikiYildiz = fiyatlar.map((fiyat) => fiyat * 3). Parantezin içindeki satır sonları ve boşluklar JavaScript için fark etmez.
map dizinin her elemanı için callback'i çağırır ve callback'in döndürdüğü değeri yeni bir dizinin aynı sırasına koyar: 4 için 12, 1 için 3, 5 için 15, 3 için 9. Sonuç her zaman yeni bir dizidir ve eleman sayısı asıl diziyle aynıdır: 4 eleman girer, 4 eleman çıkar.
Asıl dizi yerinde durur
map asıl diziye dokunmaz. İkisini birden yazdır, gör:
const fiyatlar = [4, 1, 5, 3]const ikiYildiz = fiyatlar.map((fiyat) => fiyat * 3)console.log(fiyatlar)console.log(ikiYildiz)[ 4, 1, 5, 3 ][ 12, 3, 15, 9 ]Elindeki veri bozulmadan ondan yeni şeyler türetebiliyorsun. Bu, dersin sonunda göreceğin zincirlemenin de temeli. Kendin dene: 3 yıldız için 9 kopya gerekiyor; her fiyatın 9 katını üreten bir map yaz. [ 36, 9, 45, 27 ] çıkmalı.
Obje dizisinde map
Gerçek programlarda diziler çoğunlukla obje dizisidir. Kulübendeki birimleri dosyanın başına yaz. Bu bölümde ve sonraki iki bölümde dosyanın ilk altı satırı hep bu dizi olacak; altına yazdıkların değişecek:
const kulube = [ { isim: "Ahri", yildiz: 2, maliyet: 4 }, { isim: "Garen", yildiz: 1, maliyet: 1 }, { isim: "Jinx", yildiz: 3, maliyet: 5 }, { isim: "Lux", yildiz: 1, maliyet: 3 }]Birimlerin adlarını ve satış değerlerini ayrı diziler olarak çıkar. Bu örnekte satış değeri maliyet çarpı yıldız olsun (oyundaki gerçek hesap biraz farklı):
const isimler = kulube.map((birim) => birim.isim)const degerler = kulube.map((birim) => birim.maliyet * birim.yildiz)console.log(isimler)console.log(degerler)[ 'Ahri', 'Garen', 'Jinx', 'Lux' ][ 8, 1, 15, 3 ]Callback her seferinde bir obje alıyor (birim) ve ondan tek bir değer çıkarıyor. Objelerden oluşan bir dizi, dört metin ya da dört sayıdan oluşan yeni bir diziye dönüştü.
map sonucu [ undefined, … ] çıkıyorNe zaman çıkar? map'e süslü parantezli bir callback verip return yazmayı unutunca. Program hata vermez, sessizce yanlış sonuç üretir; bu yüzden fark etmesi zordur.
const fiyatlar = [4, 1, 5, 3]const ikiYildiz = fiyatlar.map((fiyat) => { fiyat * 3})console.log(ikiYildiz)[ undefined, undefined, undefined, undefined ]Nasıl düzelir? Isınmanın ilk sorusundaki tuzak bu. Süslü parantez açınca return kendiliğinden yapılmaz; callback hiçbir şey döndürmeyince map her sıraya undefined koyar. Ya return fiyat * 3 yaz ya da süslü parantezleri kaldır: (fiyat) => fiyat * 3.
map yalnızca yazdırmak için kullanılmışNe zaman çıkar? Bir diziyi ekrana yazdırmak için forEach yerine map kullanınca. Hata mesajı çıkmaz, ekrana yazma da olur; ama map boşuna yeni bir dizi üretir.
const adlar = ["Lux", "Vi", "Zed"]const sonuc = adlar.map((ad) => console.log(ad))console.log(sonuc)LuxViZed[ undefined, undefined, undefined ]console.log bir değer döndürmediği için yeni dizinin her yeri undefined ile doldu. Kodu okuyan biri de map görünce "burada yeni bir dizi üretiliyor" diye düşünür ve yanılır.
Nasıl düzelir? Yalnızca bir iş yaptırıyorsan (yazdırmak, saymak) forEach kullan; map'i yeni bir dizi istediğinde kullan. Kural basit: map'in sonucunu bir yerde kullanmıyorsan yanlış metottasın.
filter: koşula uyanları seç
Takımın canları elinde ve yalnızca hayatta olanların canlarını istiyorsun. Ders 5'te bunu döngü, if ve push ile yapardın. filter (süzgeç) aynı işi yapar: callback true döndürürse eleman yeni diziye girer, false döndürürse girmez.
const canlar = [100, 35, 60, 0]const canlilar = []for (const can of canlar) { if (can > 0) { canlilar.push(can) }}console.log(canlilar)const canlar = [100, 35, 60, 0]const canlilar = canlar.filter( (can) => can > 0)console.log(canlilar)İkisi de aynı diziyi üretir:
[ 100, 35, 60 ]filter'ın callback'i her eleman için bir soru sorar: "bu eleman kalsın mı?" Cevap olarak true ya da false döndürür; Ders 2'deki karşılaştırmalar tam olarak bunu üretir. filter elemanları değiştirmez, yalnızca seçer. Sonuçtaki elemanlar asıl dizideki elemanların aynısıdır; sayıları ise eşit ya da daha azdır.
Obje dizisinde filter
Dosyanın başındaki kulube dizisi duruyor. Altına şunu yaz: 2 yıldız ve üstü birimler hangileri?
const gucluler = kulube.filter((birim) => birim.yildiz >= 2)console.log(gucluler)console.log(gucluler.length)[ { isim: 'Ahri', yildiz: 2, maliyet: 4 }, { isim: 'Jinx', yildiz: 3, maliyet: 5 }]2Sonuç yine obje dizisi, çünkü filter elemanları olduğu gibi seçer. Node, tek satıra sığmayan diziyi satırlara bölerek yazdı; içerik aynı.
Kaç tane olduğunu length ile sayabilirsin (Ders 5): "kulübede kaç tane 2 yıldız ve üstü birim var?" sorusunun cevabı kulube.filter(...).length. Ders 10'un görevinde yazdığın kacTane(dizi, kosul) fonksiyonunun işini artık tek satır görüyor: dizi.filter(kosul).length.
>= ile > farkına dikkat et. birim.yildiz > 2 yazsaydın 2 yıldızlı Ahri elenirdi. Ders 2'deki sınır değeri alışkanlığı burada da geçerli: koşulun tam sınırındaki elemanı (burada 2 yıldızı) mutlaka dene.
Hiçbir eleman koşula uymazsa filter hata vermez; boş bir dizi döndürür:
const dortYildiz = kulube.filter((birim) => birim.yildiz === 4)console.log(dortYildiz)console.log(dortYildiz.length)[]0Kendin dene: kulübeden maliyeti 3 ve üstü olan birimleri süz. Üç birim gelmeli: Ahri, Jinx ve Lux.
ReferenceError: birim is not definedNe zaman çıkar? filter'a (ya da map'e, find'a…) fonksiyon yerine doğrudan koşulu yazınca. birim diye bir değişken yok; o ad ancak bir fonksiyonun parametresi olunca anlam kazanır.
const kulube = [ { isim: "Ahri", yildiz: 2 }, { isim: "Garen", yildiz: 1 }]const gucluler = kulube.filter(birim.yildiz >= 2)console.log(gucluler)C:\kod\ogren\ders11.js:5const gucluler = kulube.filter(birim.yildiz >= 2) ^ ReferenceError: birim is not defined at Object.<anonymous> (C:\kod\ogren\ders11.js:5:32) ... Node.js v24.20.0Nasıl düzelir? Metoda bir fonksiyon ver: kulube.filter((birim) => birim.yildiz >= 2). Parantezi boş bırakırsan (kulube.filter()) da TypeError: undefined is not a function alırsın: metot çağıracağı bir fonksiyon bekliyordu, eline hiçbir şey geçmedi.
find ve findIndex: ilk uyanı bul
Bazen hepsini değil, yalnızca ilk uyanı istersin. Takımın canlarında 50'nin altına düşmüş ilk değer hangisi? Ders 5'te bunu döngü ve break ile yazardın. find aynı işi yapar ve bulduğu elemanı döndürür, bir dizi değil.
const canlar = [100, 35, 60, 0]let dusuk = undefinedfor (const can of canlar) { if (can < 50) { dusuk = can break }}console.log(dusuk)const canlar = [100, 35, 60, 0]const dusuk = canlar.find( (can) => can < 50)console.log(dusuk)İkisi de aynı sonucu yazar:
35find baştan başlar, callback'in true döndürdüğü ilk elemanda durur ve o elemanı döndürür. 0 da 50'nin altında ama find ona hiç bakmadı; 35'i bulunca iş bitti. Sonuç bir dizi değil, elemanın kendisi.
Obje dizisinde find
find en çok obje dizilerinde, bir elemanı adıyla ya da numarasıyla aramak için kullanılır. Kulübede Jinx var mı, varsa kaç yıldız?
const jinx = kulube.find((birim) => birim.isim === "Jinx")console.log(jinx)console.log(jinx.yildiz){ isim: 'Jinx', yildiz: 3, maliyet: 5 }3Peki aradığın birim kulübede yoksa? find hata vermez; undefined döndürür:
const teemo = kulube.find((birim) => birim.isim === "Teemo")console.log(teemo)undefinedundefined'ı Ders 3'ten ve Ders 4'ten tanıyorsun: "burada bir değer yok". Asıl tehlike, bulunamayan elemanın alanını okumaya kalkınca başlar.
TypeError: Cannot read properties of undefined (reading 'hp')Ne zaman çıkar? find aradığını bulamayıp undefined döndürdüğünde, sonucu denetlemeden bir alanını okuyunca. undefined'ın alanı olmaz; Ders 4'te gördüğün hatanın aynısı.
const takim = [ { isim: "Ahri", hp: 100 }, { isim: "Garen", hp: 0 }]const teemo = takim.find((sampiyon) => sampiyon.isim === "Teemo")console.log(teemo.hp)C:\kod\ogren\ders11.js:6console.log(teemo.hp) ^ TypeError: Cannot read properties of undefined (reading 'hp') at Object.<anonymous> (C:\kod\ogren\ders11.js:6:19) ... Node.js v24.20.0Mesaj "undefined'ın alanları okunamaz (okunmak istenen: hp)" diyor. Hata 6. satırda görünse de asıl sebep 5. satırda: find kimseyi bulamadı.
Nasıl düzelir? find'ın sonucunu kullanmadan önce bulunup bulunmadığına bak:
const takim = [ { isim: "Ahri", hp: 100 }, { isim: "Garen", hp: 0 }]const teemo = takim.find((sampiyon) => sampiyon.isim === "Teemo")if (teemo === undefined) { console.log("Teemo takımda yok")} else { console.log(teemo.hp)}Teemo takımda yokBu denetimin daha kısa bir yazılışı (?.) var; onu Ders 12'de göreceksin.
findIndex: elemanın yeri
Bazen elemanın kendisi değil, dizideki yeri gerekir: Jinx kulübenin kaçıncı yuvasında? Ders 5'te bunu i sayaçlı for döngüsüyle yazardın: let sira = -1 ile başlar, uyan elemanda sira = i yapıp break ederdin. find'ın kardeşi findIndex aynı işi yapar: aynı callback'i alır ama elemanı değil sıra numarasını döndürür; hiçbir eleman uymazsa, döngüdeki başlangıç değeri gibi, -1 verir.
const jinxSira = kulube.findIndex((birim) => birim.isim === "Jinx")const teemoSira = kulube.findIndex((birim) => birim.isim === "Teemo")console.log(jinxSira)console.log(teemoSira)console.log(kulube.indexOf("Jinx"))console.log(`Jinx ${jinxSira + 1}. yuvada`)2-1-1Jinx 3. yuvadaÇıktının üçüncü satırına dikkat: Ders 5'teki indexOf da sıra numarası verir ama bir değer arar. Kulübede "Jinx" diye bir metin yok, Jinx'i anlatan bir obje var; bu yüzden indexOf bulamadı. Elemanlar obje olunca koşul alan findIndex'i kullan.
Kendin dene: kulübede maliyeti 1 olan ilk birimi find ile bul (Garen gelmeli), sonra maliyeti 2 olanı ara (undefined gelmeli). Aynı iki aramayı findIndex ile yap: 1 ve -1 gelmeli.
map, filter ve find'ı aynı kulübe üzerinde yan yana görelim:
map her elemanı dönüştürür, filter uyanları seçer, find ilk uyanı verir ve gerisine bakmaz.some ve every: en az biri mi, hepsi mi?
Bazen elemanların kendisi değil, yalnızca "evet mi, hayır mı" cevabı gerekir. Takımda ölen var mı? Herkesin altını bir eşyaya yetiyor mu? Ders 5'te bunu false ile başlayan bir kutu, if ve break ile yapardın. some (bazısı) ve every (her biri) doğrudan true ya da false döndürür.
const canlar = [100, 35, 60, 0]let oluVar = falsefor (const can of canlar) { if (can === 0) { oluVar = true break }}console.log(oluVar)const canlar = [100, 35, 60, 0]const oluVar = canlar.some( (can) => can === 0)console.log(oluVar)İkisinin cevabı da aynı:
truesome "en az biri uyuyor mu?" diye sorar: callback'in true döndürdüğü ilk elemanda durur ve true verir; hiçbiri uymazsa false. every "hepsi uyuyor mu?" diye sorar: ilk uymayan elemanda durur ve false verir; hepsi uyarsa true. every'nin döngü hâli yukarıdakinin tersidir: kutu true ile başlar, ilk uymayan elemanda false olur ve break gelir.
Dersin başındaki takımı dosyanın başına geri yaz. Bu bölümde ve bir sonrakinde dosyanın ilk altı satırı bu olacak:
const takim = [ { isim: "Ahri", hp: 100, gold: 300 }, { isim: "Garen", hp: 0, gold: 450 }, { isim: "Jinx", hp: 60, gold: 1200 }, { isim: "Lux", hp: 0, gold: 800 }]const oluVar = takim.some((sampiyon) => sampiyon.hp === 0)const hepsiZengin = takim.every((sampiyon) => sampiyon.gold >= 300)const hepsiCanli = takim.every((sampiyon) => sampiyon.hp > 0)console.log(oluVar)console.log(hepsiZengin)console.log(hepsiCanli)truetruefalseAhri'nin altını tam 300; >= kullandığımız için o da uydu ve hepsiZengin true çıktı. > yazsaydın false olurdu. Sınırı yine dene.
some'ı Ders 5'teki includes ile karıştırma. İkisi de true ya da false verir, ama includes bir değeri arar (adlar.includes("Lux")), some ise bir koşulu. Basit bir değer arıyorsan includes, koşul gerekiyorsa some.
Kendin dene: takımda altını 1000'den fazla olan biri var mı (true çıkmalı)? Herkesin canı 100 mü (false çıkmalı)?
reduce: hepsini tek değere indir
Şimdiye kadarki metotlar bir dizi, tek bir eleman, bir sıra numarası ya da true/false döndürdü. Bazen bütün diziyi tek bir değere indirmek istersin: toplam altın, en zengin şampiyon, bölgelere göre gruplar. Ders 5'te bunun için döngüden önce bir let toplam = 0 kutusu açardın. reduce (indirgemek) bu kutuyu da kendisi taşır.
const altinlar = [300, 450, 800]let toplam = 0for (const altin of altinlar) { toplam = toplam + altin}console.log(toplam)const altinlar = [300, 450, 800]const toplam = altinlar.reduce( (birikim, altin) => { return birikim + altin }, 0)console.log(toplam)İkisi de aynı toplamı bulur:
1550reduce'a iki şey veriyorsun: bir callback ve bir başlangıç değeri (burada 0). Döngüdeki let toplam = 0 satırının karşılığı bu başlangıç değeri. Callback'in iki parametresi var: o ana kadar biriken değer, yani biriktirici (birikim), ve sıradaki eleman (altin). Her turda callback'in döndürdüğü değer, bir sonraki turun birikim'i olur; dizi bitince son dönen değer reduce'un sonucudur.
reduce adım adım: biriktirici 0'dan başlar, her turda bir eleman eklenir, son değer sonuçtur.Aynı turlar tablo olarak:
| Tur | Gelen biriktirici | Sıradaki eleman | Callback'in döndürdüğü |
|---|---|---|---|
| 1 | 0 (başlangıç değeri) | 300 | 300 |
| 2 | 300 | 450 | 750 |
| 3 | 750 | 800 | 1550 |
Kısa yazılış ve obje dizisi
Callback tek bir hesaptan ibaretse süslü parantez ve return gerekmez. Dosyanın başındaki takımın toplam altını:
const toplamAltin = takim.reduce( (toplam, sampiyon) => toplam + sampiyon.gold, 0)console.log(toplamAltin)2750reduce'un parantezinde iki argüman var, virgülle ayrılmış: önce callback, sonra başlangıç değeri 0. Tek satıra yazınca kolayca karışır: takim.reduce((toplam, sampiyon) => toplam + sampiyon.gold, 0). Buradaki , 0 callback'in parçası değil, reduce'un ikinci argümanı; en sondaki ) ise reduce'un parantezini kapatır. Kendin dene: takımın toplam canını hesapla; 160 çıkmalı.
TypeError: Reduce of empty array with no initial valueNe zaman çıkar? Boş bir dizide, başlangıç değeri vermeden reduce çağırınca. Başlangıç değeri yoksa reduce dizinin ilk elemanını başlangıç sayar; dizi boşsa başlayacak bir şey bulamaz.
const altinlar = []const toplam = altinlar.reduce((birikim, altin) => birikim + altin)console.log(toplam)C:\kod\ogren\ders11.js:2const toplam = altinlar.reduce((birikim, altin) => birikim + altin) ^ TypeError: Reduce of empty array with no initial value at Array.reduce (<anonymous>) at Object.<anonymous> (C:\kod\ogren\ders11.js:2:25) ... Node.js v24.20.0Mesaj kelimesi kelimesine durumu anlatıyor: "boş dizide, başlangıç değeri olmadan reduce".
Nasıl düzelir? Başlangıç değerini yaz: altinlar.reduce((birikim, altin) => birikim + altin, 0). Boş dizide callback hiç çağrılmaz ve sonuç doğrudan başlangıç değeri olur: 0. Hiç altın yoksa toplam altın 0'dır; istediğin de bu.
En büyüğü bulmak
Biriktirici yalnızca bir toplam olmak zorunda değil. Takımın en zengin şampiyonunu bulalım; bu kez biriktirici "şimdiye kadarki en zengin" şampiyonun objesi. Ders 5'teki let enYuksek = takim[0] döngüsüyle aynı fikir:
const enZengin = takim.reduce((enIyi, sampiyon) => { if (sampiyon.gold > enIyi.gold) { return sampiyon } return enIyi})console.log(enZengin){ isim: 'Jinx', hp: 60, gold: 1200 }Her turda iki şampiyonu karşılaştırıp daha zengin olanı döndürüyoruz; dönen, bir sonraki turun enIyi'si oluyor. Bu kez başlangıç değeri vermedik: reduce ilk elemanı (Ahri) başlangıç aldı ve ikinci elemandan başladı. En büyüğü ararken bu kullanışlı, ama yukarıdaki hatayı hatırla: takım boşsa program çöker. Boş olabilecek bir dizide önce takim.length === 0 mı diye bak.
Gruplamak
Biriktiriciyi boş bir obje olarak başlatıp turdan tura doldurabilirsin de. Şampiyonları bölgelerine göre gruplayalım: sonuç, her bölgenin adı altında o bölgeden şampiyonların listesini tutan bir obje olsun. Bu örnek tek başına bir program; dosyandakini silip yaz:
const sampiyonlar = [ { isim: "Ahri", bolge: "Ionia" }, { isim: "Garen", bolge: "Demacia" }, { isim: "Yasuo", bolge: "Ionia" }, { isim: "Lux", bolge: "Demacia" }, { isim: "Jinx", bolge: "Zaun" }] const bolgeler = sampiyonlar.reduce((gruplar, sampiyon) => { if (gruplar[sampiyon.bolge] === undefined) { gruplar[sampiyon.bolge] = [] } gruplar[sampiyon.bolge].push(sampiyon.isim) return gruplar}, {}) console.log(bolgeler){ Ionia: [ 'Ahri', 'Yasuo' ], Demacia: [ 'Garen', 'Lux' ], Zaun: [ 'Jinx' ]}Adım adım ne oluyor:
- Başlangıç değeri boş bir obje:
{}(15. satırın sonunda). - Her şampiyonda, onun bölgesi için grupta henüz liste yoksa (
undefined, Ders 4) boş bir liste açılıyor. Anahtarın adı bir değişkende durduğu için nokta değil köşeli parantez kullandık:gruplar[sampiyon.bolge]. - Şampiyonun adı o bölgenin listesine
pushediliyor. - Biriktirici (
gruplar) mutlaka geri döndürülüyor; bir sonraki tur onunla devam edecek.
TypeError: Cannot read properties of undefined (reading 'Demacia')Ne zaman çıkar? reduce'un callback'inde biriktiriciyi return etmeyi unutunca. İlk tur sorunsuz geçer ama hiçbir şey döndürmediği için ikinci turda biriktirici undefined olur.
const sampiyonlar = [ { isim: "Ahri", bolge: "Ionia" }, { isim: "Garen", bolge: "Demacia" }]const bolgeler = sampiyonlar.reduce((gruplar, sampiyon) => { if (gruplar[sampiyon.bolge] === undefined) { gruplar[sampiyon.bolge] = [] } gruplar[sampiyon.bolge].push(sampiyon.isim)}, {})console.log(bolgeler)C:\kod\ogren\ders11.js:6 if (gruplar[sampiyon.bolge] === undefined) { ^ TypeError: Cannot read properties of undefined (reading 'Demacia') at C:\kod\ogren\ders11.js:6:16 at Array.reduce (<anonymous>) ... Node.js v24.20.0İpucu mesajın içinde: okunmak istenen alan 'Demacia', yani ikinci şampiyonun bölgesi. Birinci turda sorun yoktu; ikinci turda gruplar artık undefined'dı. at Array.reduce satırı da hatanın reduce'un çağırdığı callback'in içinde çıktığını söylüyor.
Nasıl düzelir? Callback'in son satırı her zaman return gruplar olmalı. reduce callback'inden ne döndürürsen bir sonraki tur onu alır.
sort: sıralamak
Maç sonu skor tablosunu düşün: oyuncular puana göre büyükten küçüğe dizilir. Sıralamayı döngüyle yazmak (sıralama algoritmaları) ayrı ve uzun bir konu; mülakatlarda sorulur ama günlük işte herkes hazır sort metodunu kullanır. Bu yüzden bu bölümde döngülü karşılaştırma yok. Yalnız sort'un iki sürprizi var ve ikisi de yeni başlayanları sık sık yakalar.
Sürpriz 1: sayıları metin gibi sıralar
const sayilar = [10, 9, 1]sayilar.sort()console.log(sayilar)[ 1, 10, 9 ]10, 9'dan önce geldi. Sebep: sort'a nasıl sıralayacağını söylemezsen elemanları metne çevirir ve metinleri sözlükteki gibi soldan sağa, karakter karakter karşılaştırır. "10" ile "9" karşılaştırılırken önce ilk karakterlere, "1" ile "9"'a bakılır; "1" önde olduğu için "10" öne geçer. Sözlükte "araba"nın "su"dan önce gelmesi gibi: kelimenin uzunluğuna değil, ilk harfine bakılır.
"1" ile "10"'un ise ilk karakteri aynı. O zaman sıradaki karaktere geçilir; "1" orada bittiği için önce o gelir, sözlükte "ara"nın "araba"dan önce gelmesi gibi.
sort() sayıları önce metne çevirir, sonra sözlük gibi ilk karakterlerine bakarak dizer.Karşılaştırma fonksiyonu
Sayıları sayı gibi sıralatmak için sort'a iki elemanı karşılaştıran bir fonksiyon verirsin. sort sıralarken elemanları ikişer ikişer bu fonksiyona sorar; fonksiyon bir sayı döndürür:
- Eksi bir sayı:
aönce gelsin. - Artı bir sayı:
bönce gelsin. - 0: ikisi eşit, sıralarını değiştirme.
Küçükten büyüğe sıralamanın kısa yolu (a, b) => a - b: a küçükse çıkarmanın sonucu eksi olur ve a öne geçer. Büyükten küçüğe için çıkarmayı ters çevirirsin: (a, b) => b - a.
const sayilar = [10, 9, 1]sayilar.sort((a, b) => a - b)console.log(sayilar)sayilar.sort((a, b) => b - a)console.log(sayilar)[ 1, 9, 10 ][ 10, 9, 1 ]Birkaç karşılaştırmada a - b ne der:
| a | b | a - b | sort ne yapar? |
|---|---|---|---|
10 | 9 | 1 (artı) | 9 öne geçer |
1 | 9 | -8 (eksi) | 1 önde kalır |
9 | 9 | 0 | yerleri değişmez |
Burada tek harfli a ve b herkesin kullandığı bir alışkanlık; "birinci" ve "ikinci" diye oku. Bu iki kalıbı ezberlemen yeterli, her seferinde eksi ve artı hesabı yapman gerekmez.
sort diziyi hiç sıralamadıNe zaman çıkar? Karşılaştırma fonksiyonu sayı yerine true ya da false döndürünce: (a, b) => a > b. Eski öğreticilerde bu yazımı görebilirsin. Node'da hiçbir şeyi sıralamaz, hata da vermez:
const sayilar = [10, 9, 1]sayilar.sort((a, b) => a > b)console.log(sayilar)[ 10, 9, 1 ]Nasıl düzelir? sort eksi, artı ya da 0 bekler. > ile değil, çıkarmayla karşılaştır: (a, b) => a - b.
Skor tablosu: objeleri sıralamak
Objeleri sıralarken karşılaştırma fonksiyonunda alanları kullanırsın. Puanı yüksek olan önde olsun:
const skorlar = [ { oyuncu: "Deniz", puan: 1450 }, { oyuncu: "Ece", puan: 2300 }, { oyuncu: "Can", puan: 980 }, { oyuncu: "Selin", puan: 2300 }] skorlar.sort((a, b) => b.puan - a.puan)skorlar.forEach((satir, sira) => { console.log(`${sira + 1}. ${satir.oyuncu}: ${satir.puan}`)})1. Ece: 23002. Selin: 23003. Deniz: 14504. Can: 980Puanları eşit olan Ece ile Selin dizideki eski sıralarını korudu: sort eşit elemanların yerini değiştirmez. Kendin dene: skorları küçükten büyüğe sırala; ilk satırda Can olmalı.
Sürpriz 2: sort asıl diziyi değiştirir
map ve filter asıl diziye dokunmuyordu. sort dokunur: diziyi yerinde sıralar ve geriye aynı diziyi verir. Yukarıdaki skor tablosunda skorlar dizisinin sırası kalıcı olarak değişti. Çoğu zaman sorun yok; ama asıl sırayı başka bir yerde kullanacaksan sessizce bozulmuş olur.
sort asıl diziyi sessizce değiştirdiNe zaman çıkar? Sıralanmış bir kopya isteyip sort'u doğrudan asıl dizide çağırınca. Hata mesajı çıkmaz; asıl dizinin sırası değişir ve ona güvenen kod yanlış çalışır.
const macPuanlari = [40, 95, 70]const sirali = macPuanlari.sort((a, b) => b - a)console.log(sirali)console.log(macPuanlari)[ 95, 70, 40 ][ 95, 70, 40 ]Puanları maç sırasıyla tutuyordun: ilk maçta 40, ikincide 95, üçüncüde 70. Sıralı bir liste istedin ama asıl dizi de değişti; hangi maçta kaç puan aldığın artık bilinmiyor. sirali ile macPuanlari iki ayrı dizi değil, aynı dizinin iki adı. Bunun nasıl olduğunu Ders 12'de (kopya ve referans) göreceksin.
Nasıl düzelir? Asıl dizi değişmesin istiyorsan sort yerine toSorted kullan.
toSorted: sıralı bir kopya
toSorted (sıralanmış hâlini ver) sort ile aynı karşılaştırma fonksiyonunu alır ama asıl diziye dokunmaz; sıralı yepyeni bir dizi döndürür:
const macPuanlari = [40, 95, 70]const sirali = macPuanlari.toSorted((a, b) => b - a)console.log(sirali)console.log(macPuanlari)[ 95, 70, 40 ][ 40, 95, 70 ]toSorted JavaScript'e 2023'te eklendi; Node.js'te 20. sürümden beri var, senin Node 24'ünde sorunsuz çalışır. Eski öğreticilerde aynı iş için [...dizi].sort() gibi yazımlar görürsen şaşırma: o da önce kopyalayıp sonra sıralamanın eski yolu; üç noktanın ne olduğunu Ders 12'de öğreneceksin.
sort aynı diziyi yerinde değiştirir; toSorted asıl diziyi bırakıp sıralı yeni bir dizi verir.Değişmezlik: hangi metot asıl diziye dokunur?
Bu derste gördüğün metotların çoğu asıl diziyi olduğu gibi bırakıp sonucu yeni bir değer olarak verir. Aralarında asıl diziyi değiştiren tek metot sort; Ders 5'ten tanıdığın push ve pop da diziyi değiştirir. Bu ayrımı bilmek, "verim kendi kendine değişti" türünden en sinsi hataları önler.
Zincirleme
Ders 9'da metinlerde zincirleme gördün: mesaj.trim().toLowerCase(). Diziler de aynı şekilde zincirlenir. filter ve map yeni bir dizi döndürdüğü için sonucun üstünde hemen başka bir metot çağırabilirsin. Dersin başındaki "canlıların adları" satırı tam buydu: önce süz, sonra dönüştür. Takım dizisini yine dosyanın başına yaz:
const takim = [ { isim: "Ahri", hp: 100, gold: 300 }, { isim: "Garen", hp: 0, gold: 450 }, { isim: "Jinx", hp: 60, gold: 1200 }, { isim: "Lux", hp: 0, gold: 800 }]const canliAdlari = takim .filter((sampiyon) => sampiyon.hp > 0) .map((sampiyon) => sampiyon.isim)console.log(canliAdlari)[ 'Ahri', 'Jinx' ]Satırları yukarıdan aşağı oku: takımı al, canlıları süz, adlarına dönüştür. Her nokta, bir önceki adımın sonucu üzerinde çalışır: filter iki şampiyonluk yeni bir dizi verdi, map o dizinin üstünde çalıştı. Satır başındaki nokta JavaScript'i şaşırtmaz; satır noktayla başlayınca bir önceki satırın devamı sayılır.
Zincire bir halka daha ekle: canlıların toplam altını.
const canliAltin = takim .filter((sampiyon) => sampiyon.hp > 0) .map((sampiyon) => sampiyon.gold) .reduce((toplam, altin) => toplam + altin, 0)console.log(canliAltin)1500Halkaların sırası önemli. Önce map ile altınlara dönüştürseydin elinde yalnızca sayılar kalırdı ve hangisinin canlı olduğunu bilemezdin. Zincirlerin çoğu bu sırayı izler: önce süz (filter), sonra dönüştür (map), en son indir (reduce).
Dizi zincirlerinde her metot genellikle kendi satırına, başında noktayla yazılır; böylece her halka ayrı okunur. Zincir uzadıkça okunması yine de zorlaşır. Dört beş halkayı geçince ya da bir adımın sonucunu görmek istediğinde, ara adımlara ad ver: const canlilar = takim.filter(...), sonra const altinlari = canlilar.map(...). Hata ayıklarken (Ders 7) ara sonucu console.log ile görmek çok işine yarar.
TypeError: Cannot read properties of undefined (reading 'map')Ne zaman çıkar? Zincirin ortasına forEach koyunca. forEach undefined döndürür; zincir orada kopar ve bir sonraki halka undefined'ın üstünde çalışmaya kalkar.
const adlar = ["Lux", "Vi", "Zed"]const etiketler = adlar .forEach((ad) => console.log(ad)) .map((ad) => `${ad} hazır`)LuxViZedC:\kod\ogren\ders11.js:4 .map((ad) => `${ad} hazır`) ^ TypeError: Cannot read properties of undefined (reading 'map') at Object.<anonymous> (C:\kod\ogren\ders11.js:4:5) ... Node.js v24.20.0forEach işini yaptı (üç ad yazıldı), sonra undefined döndürdü ve .map onun üstünde çağrıldı. Mesajdaki (reading 'map') tam olarak bunu söylüyor: undefined'ın map'i okunamaz.
Nasıl düzelir? forEach zincirin yalnızca en sonunda durabilir. Ara halkalarda değer döndüren metotları (filter, map, toSorted) kullan, ekrana yazdırmayı en sona bırak.
Kendin dene: ölü şampiyonların (canı 0 olanların) adlarını zincirle bul. [ 'Garen', 'Lux' ] çıkmalı.
Hangi durumda hangisi?
Bir iş için metot seçerken kendine önce şunu sor: sonuç olarak ne istiyorum? Bir dizi mi, tek bir eleman mı, evet/hayır mı, tek bir değer mi? Dizi istiyorsan ikinci soru: eleman sayısı aynı mı kalacak (map) yoksa azalacak mı (filter)?
| Ne istiyorsun? | Metot | Ne döndürür? |
|---|---|---|
| Her eleman için bir iş yap (yazdır, say) | forEach | undefined |
| Her elemandan yeni bir değer üret | map | aynı uzunlukta yeni dizi |
| Koşula uyanları seç | filter | yeni dizi (boş olabilir) |
| Koşula uyan ilk elemanı bul | find | eleman ya da undefined |
| Koşula uyan ilk elemanın yerini bul | findIndex | sıra numarası ya da -1 |
| En az biri uyuyor mu? | some | true / false |
| Hepsi uyuyor mu? | every | true / false |
| Hepsini tek değere indir (toplam, en büyük, gruplar) | reduce | tek değer |
| Sırala, asıl dizi bozulmasın | toSorted | sıralı yeni dizi |
| Sırala, asıl dizi de sıralansın | sort | aynı dizi (değişmiş) |
| Belli bir değer var mı? (Ders 5) | includes | true / false |
Tablodaki metotlardan asıl diziyi değiştiren tek metot sort. Bir de şunu aklında tut: find, findIndex, some ve every cevap belli olur olmaz durur, dizinin gerisine bakmaz; map, filter ve reduce ise her elemana mutlaka uğrar.
Görevler
Görevleri ders11.js dosyasında yap; her görevde eski kodu sil ya da satırların başına // koy. Bu görevlerde for ve while yazmak yasak: hepsini dizi metotlarıyla çöz. Takılırsan ipuçlarını sırayla aç, çözüme en son bak.
Mana iksiri
Takımın mana değerleri [120, 40, 0, 75, 50]. Herkes bir mana iksiri içiyor ve manasına 30 ekleniyor: iksirden sonraki değerleri yeni bir dizi olarak üret. Sonra iksirden önceki değerlere göre manası 50'nin altında olanları seç. En sonda asıl dizinin değişmediğini göstermek için onu da yazdır. Çıktı:
[ 150, 70, 30, 105, 80 ][ 40, 0 ][ 120, 40, 0, 75, 50 ]İpucu 1
Karar tablosuna bak: "her elemandan yeni bir değer" hangi metot, "koşula uyanları seç" hangisi?
İpucu 2
manalar.map((mana) => ...) ve manalar.filter((mana) => ...). "50'nin altında" 50'yi içermez: < mü, <= mü?
Çözümü göster
const manalar = [120, 40, 0, 75, 50]const iksirli = manalar.map((mana) => mana + 30)const azManali = manalar.filter((mana) => mana < 50)console.log(iksirli)console.log(azManali)console.log(manalar)[ 150, 70, 30, 105, 80 ][ 40, 0 ][ 120, 40, 0, 75, 50 ]Sınır denemesi: 50 ikinci satırda yok, çünkü "altında" 50'yi içermez; <= yazsaydın [ 40, 0, 50 ] çıkardı. 0 ise listede, o da 50'nin altında. map ile filter asıl diziye dokunmadığı için son satır ilk hâliyle duruyor.
İlk büyük maç
Son maçların aşağıda. Bir eşik alan ilkKillMaci(esik) fonksiyonu yaz: o eşik kadar ya da daha fazla kill aldığın ilk maçın şampiyonunu ve kaçıncı maç olduğunu yazdırsın. Böyle bir maç yoksa program çökmesin, bunu söylesin.
const maclar = [ { sampiyon: "Lux", kill: 3 }, { sampiyon: "Annie", kill: 0 }, { sampiyon: "Garen", kill: 10 }, { sampiyon: "Yasuo", kill: 12 }]ilkKillMaci(10) ve ilkKillMaci(20) çağrıları şunu yazmalı:
İlk 10+ kill: Garen, 3. maç20+ kill aldığın maç yokİpucu 1
İlk uyanı bulmak find'ın, yerini bulmak findIndex'in işi.
İpucu 2
find bulamazsa ne döndürüyordu? Sonucun bir alanını okumadan önce onu denetle; bulunamadıysa mesajı yazıp return ile fonksiyondan erken çık (Ders 6).
İpucu 3
"Kaçıncı maç": findIndex 0'dan sayar, ekrana yazarken 1 ekle.
Çözümü göster
const maclar = [ { sampiyon: "Lux", kill: 3 }, { sampiyon: "Annie", kill: 0 }, { sampiyon: "Garen", kill: 10 }, { sampiyon: "Yasuo", kill: 12 }] function ilkKillMaci(esik) { const bulunan = maclar.find((mac) => mac.kill >= esik) if (bulunan === undefined) { console.log(`${esik}+ kill aldığın maç yok`) return } const sira = maclar.findIndex((mac) => mac.kill >= esik) console.log(`İlk ${esik}+ kill: ${bulunan.sampiyon}, ${sira + 1}. maç`)} ilkKillMaci(10)ilkKillMaci(20)İlk 10+ kill: Garen, 3. maç20+ kill aldığın maç yokSınır denemesi: Garen tam 10 kill aldı ve sonuç o; > yazsaydın Yasuo (4. maç) çıkardı. ilkKillMaci(0) dene: ilk maç (Lux) gelmeli, çünkü 3 de 0'dan büyük ya da eşit. Denetimi yazmasaydın ilkKillMaci(20) çağrısı Cannot read properties of undefined (reading 'sampiyon') ile çökerdi.
Maç bulundu
Lobideki her oyuncunun durumu "kabul", "red" ya da "bekliyor". Bir lobi dizisi alan lobiDurumu(lobi) fonksiyonu yaz ve şu metinlerden birini döndürsün (yazdırmak değil, return):
- lobi boşsa
Lobi boş - en az bir oyuncu reddettiyse
Biri reddetti, kuyruğa dönülüyor - herkes kabul ettiyse
Maç başlıyor - değilse
Bekleniyor: N oyuncu(N, bekleyenlerin sayısı)
Dört lobiyle dene; çağrıları kendin yaz:
const hazirLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "kabul" }, { ad: "Can", durum: "kabul" }]const redLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "red" }, { ad: "Can", durum: "bekliyor" }]const bekleyenLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "bekliyor" }, { ad: "Can", durum: "bekliyor" }]Bu üç lobi ve boş bir lobi ([]) için çıktı:
Maç başlıyorBiri reddetti, kuyruğa dönülüyorBekleniyor: 2 oyuncuLobi boşİpucu 1
"En az biri" hangi metot, "hepsi" hangisi? Koşulları Ders 3'teki else if zinciriyle sırala.
İpucu 2
Boş dizide every ne döndürüyordu? Boş lobi denetimini koşulların en başına koy, yoksa boş lobi "Maç başlıyor" der.
İpucu 3
Bekleyenleri saymak için filter(...).length.
Çözümü göster
function lobiDurumu(lobi) { if (lobi.length === 0) { return "Lobi boş" } else if (lobi.some((oyuncu) => oyuncu.durum === "red")) { return "Biri reddetti, kuyruğa dönülüyor" } else if (lobi.every((oyuncu) => oyuncu.durum === "kabul")) { return "Maç başlıyor" } const bekleyenler = lobi.filter((oyuncu) => oyuncu.durum === "bekliyor") return `Bekleniyor: ${bekleyenler.length} oyuncu`} const hazirLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "kabul" }, { ad: "Can", durum: "kabul" }]const redLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "red" }, { ad: "Can", durum: "bekliyor" }]const bekleyenLobi = [ { ad: "Deniz", durum: "kabul" }, { ad: "Ece", durum: "bekliyor" }, { ad: "Can", durum: "bekliyor" }] console.log(lobiDurumu(hazirLobi))console.log(lobiDurumu(redLobi))console.log(lobiDurumu(bekleyenLobi))console.log(lobiDurumu([]))Maç başlıyorBiri reddetti, kuyruğa dönülüyorBekleniyor: 2 oyuncuLobi boşSınır denemesi: boş lobi denetimini en sona taşı ve çalıştır; son satır Maç başlıyor olur, çünkü boş dizide every true döndürür. redLobi'de bekleyen biri olduğu hâlde "reddetti" mesajı geldi: koşulların sırası, hangi durumun daha önemli olduğunu belirler.
Şampiyon sayacı
Son maçlarında oynadığın şampiyonlar bir dizide: ["Ahri", "Lux", "Ahri", "Jinx", "Ahri", "Lux"]. Bir dizi alıp her şampiyonla kaç maç oynadığını sayan ve sonucu obje olarak döndüren macSayilari(sampiyonlar) fonksiyonunu reduce ile yaz. Bu dizi, tek maçlık ["Teemo"] ve boş bir dizi için çıktı:
{ Ahri: 3, Lux: 2, Jinx: 1 }{ Teemo: 1 }{}İpucu 1
Gruplama örneğine benziyor, ama listeye ad eklemek yerine bir sayıyı 1 artıracaksın. Başlangıç değeri boş obje: {}.
İpucu 2
Şampiyonun sayacı henüz yoksa (=== undefined) önce 0 yap, sonra 1 artır. Callback'in sonunda biriktiriciyi return etmeyi unutma.
Çözümü göster
const gecmis = ["Ahri", "Lux", "Ahri", "Jinx", "Ahri", "Lux"] function macSayilari(sampiyonlar) { return sampiyonlar.reduce((sayac, isim) => { if (sayac[isim] === undefined) { sayac[isim] = 0 } sayac[isim] = sayac[isim] + 1 return sayac }, {})} console.log(macSayilari(gecmis))console.log(macSayilari(["Teemo"]))console.log(macSayilari([])){ Ahri: 3, Lux: 2, Jinx: 1 }{ Teemo: 1 }{}Sınır denemesi: boş dizide callback hiç çağrılmadı ve başlangıç değeri {} olduğu gibi döndü; başlangıç değerini yazmasaydın burada Reduce of empty array with no initial value hatası alırdın. return sayac satırını silip dene: ikinci turda Cannot read properties of undefined hatası gelir.
LP tablosu
Lig puanı (LP) tablosu aşağıda. Bir liste alan ilkUc(liste) fonksiyonu yaz: oyuncuları LP'ye göre büyükten küçüğe sıralayıp ilk üçünü 1. Ece (92 LP) biçiminde yazdırsın. Kurallar:
- LP'leri eşit olanlar Türk alfabesine göre sıralansın.
- Asıl liste değişmesin.
- Listede üçten az oyuncu varsa kaç kişi varsa o kadar satır yazılsın.
const oyuncular = [ { ad: "Mert", lp: 64 }, { ad: "Ece", lp: 92 }, { ad: "Deniz", lp: 75 }, { ad: "Zeynep", lp: 40 }, { ad: "Çağla", lp: 75 }]const ikiKisi = [ { ad: "Can", lp: 18 }, { ad: "Ali", lp: 33 }]Fonksiyonu önce oyuncular, sonra ikiKisi ile çağır; en sonda asıl listenin ilk oyuncusunun adını yazdır. Aradaki --- satırlarını da sen yazdır. Beklenen çıktı:
1. Ece (92 LP)2. Çağla (75 LP)3. Deniz (75 LP)---1. Ali (33 LP)2. Can (18 LP)---Asıl listenin ilki hâlâ: Mertİpucu 1
Asıl liste değişmeyecekse sort değil toSorted. Yazdırmak için forEach'in ikinci parametresini (sıra numarası) kullan.
İpucu 2
Karşılaştırma fonksiyonunda süslü parantez aç: LP'ler farklıysa return b.lp - a.lp; eşitse adları a.ad.localeCompare(b.ad, "tr") ile karşılaştırıp onu döndür.
İpucu 3
İlk üçü almak için filter'ın da ikinci parametresi var: (oyuncu, sira) => sira < 3. Sıralı listeye filter ve forEach'i zincirle.
Çözümü göster
const oyuncular = [ { ad: "Mert", lp: 64 }, { ad: "Ece", lp: 92 }, { ad: "Deniz", lp: 75 }, { ad: "Zeynep", lp: 40 }, { ad: "Çağla", lp: 75 }]const ikiKisi = [ { ad: "Can", lp: 18 }, { ad: "Ali", lp: 33 }] function ilkUc(liste) { const sirali = liste.toSorted((a, b) => { if (a.lp !== b.lp) { return b.lp - a.lp } return a.ad.localeCompare(b.ad, "tr") }) sirali .filter((oyuncu, sira) => sira < 3) .forEach((oyuncu, sira) => { console.log(`${sira + 1}. ${oyuncu.ad} (${oyuncu.lp} LP)`) })} ilkUc(oyuncular)console.log("---")ilkUc(ikiKisi)console.log("---")console.log(`Asıl listenin ilki hâlâ: ${oyuncular[0].ad}`)1. Ece (92 LP)2. Çağla (75 LP)3. Deniz (75 LP)---1. Ali (33 LP)2. Can (18 LP)---Asıl listenin ilki hâlâ: MertSınır denemesi: Çağla ile Deniz'in LP'si eşit. localeCompare satırını return 0 yaparsan Deniz öne geçer, çünkü listede önce o var ve toSorted da sort gibi eşitlerin yerini değiştirmez. Türkçe adları sıralama tuzağını da hatırla: adlar localeCompare yerine harf kodlarına göre dizilseydi Ç, D'nin arkasına düşerdi.
İki kişilik listede sira < 3 iki kişiyi de geçirdi, hata çıkmadı. toSorted yerine sort yazıp dene: son satır Mert değil Ece olur.
Kendini sına
Soru 1
Bu program ne yazar?
console.log([3, 20, 100].sort())Cevabı göster
[ 100, 20, 3 ]. Karşılaştırma fonksiyonu verilmediği için sayılar metne çevrilip sözlük sırasına dizilir: "100", "20", "3"; ilk karakterler 1, 2 ve 3. Doğru sıralama için (a, b) => a - b vermek gerekir.
Soru 2
yeni dizisinde ne var?
const seviyeler = [1, 2, 3]const yeni = seviyeler.map((seviye) => { seviye + 1 })console.log(yeni)Cevabı göster
[ undefined, undefined, undefined ]. Callback süslü parantezli ama return yok, bu yüzden her eleman için undefined döner. Düzeltmek için ya return seviye + 1 yaz ya da süslü parantezleri kaldır: (seviye) => seviye + 1.
Soru 3
Koşula hiçbir eleman uymazsa find, findIndex ve filter ne döndürür?
Cevabı göster
find undefined, findIndex -1, filter boş dizi ([]) döndürür. Üçü de hata vermez; bu yüzden sonucu kullanmadan önce denetlemek senin işin.
Soru 4
Bu program ne yazar ve neden?
const takim = [{ gold: 300 }, { gold: 450 }]const toplam = takim.reduce((birikim, sampiyon) => birikim + sampiyon.gold)console.log(toplam)Cevabı göster
[object Object]450. Başlangıç değeri verilmediği için reduce ilk elemanı, yani { gold: 300 } objesini başlangıç aldı. İlk turda bir obje ile 450 toplanınca JavaScript objeyi metne çevirdi ("[object Object]") ve 450'yi yanına yapıştırdı (Ders 1'deki metin + sayı kuralı). Düzeltmek için başlangıç değerini yaz: reduce(..., 0). Sonuç 750 olur.
Soru 5
Hangi metodu kullanırsın?
- Takımda 10'dan fazla kill alan biri var mı?
- Her şampiyonun adını büyük harfle yazılmış hâliyle yeni bir diziye koymak.
- Manası 100'ün altında olan şampiyonları seçmek.
- Takımın toplam kill sayısı.
- Adı "Teemo" olan şampiyonun objesi.
Cevabı göster
some: "en az biri" sorusu, cevabıtrue/false.map: her elemandan yeni bir değer, eleman sayısı aynı ((sampiyon) => sampiyon.isim.toUpperCase()).filter: uyanları seçmek.reduce: hepsini tek değere indirmek, başlangıç değeri0.find: koşula uyan ilk eleman; bulunamazsaundefinedgelir, denetle.
Soru 6
Bu zincir ne yazar?
const sonuc = [5, 12, 8, 30] .filter((sayi) => sayi > 6) .map((sayi) => sayi / 2)console.log(sonuc)Cevabı göster
[ 6, 4, 15 ]. Önce filter 6'dan büyükleri bırakır: [ 12, 8, 30 ]. Sonra map her birini ikiye böler: [ 6, 4, 15 ]. 5 ilk halkada elendiği için ikinci halkaya hiç ulaşmadı.
Soru 7
puanlar en sonda nasıl görünür? toSorted yazılsaydı ne değişirdi?
const puanlar = [3, 1, 2]const sirali = puanlar.sort((a, b) => a - b)console.log(puanlar)Cevabı göster
[ 1, 2, 3 ]. sort diziyi yerinde sıralar; sirali ile puanlar aynı dizi olur. toSorted yazılsaydı sirali yine [ 1, 2, 3 ] olurdu ama puanlar [ 3, 1, 2 ] olarak kalırdı.
Kayıt noktası
Dersin kodunu Git'e kaydet. VS Code terminalinde, C:\kod\ogren klasöründeyken:
Bir sonraki derste objelerle ve dizilerle daha rahat çalışmanın yollarını öğreneceksin: parçalama (destructuring), yayma (...), kopya ile referans farkı ve JSON; bu dersteki "callback'in içinde elemanı değiştirme" kuralının nedeni de orada.