25 Haziran 2023

Dizi Metodları

Diziler birçok metod sunarlar. İşleri daha kolaylaştırmak için bu bölüm ikiye ayrılacaktır.

Elaman silme/ekleme

Eleman eklemenin ve silmenin baştan ve sondan nasıl olacağını gördünüz:

  • arr.push(...items) – elemanları sona ekler,
  • arr.pop() – sondaki elemanı çıkarır.
  • arr.shift() – başlangıçtan eleman çıkarır.
  • arr.unshift(...items) – başlangıça eleman ekler.

Diğer birkaç metod ise aşağıdaki gibidir.

splice

Diziden eleman nasıl silinir?

Diziler de obje olduklarından dolayı, delete kullanarak silinebilir.

let arr = ["eve", "gitmek", "istiyorum"];

delete arr[1]; // "gitmek" silinecek

alert( arr[1] ); // undefined

// şimdi arr = ["eve",  , "istiyorum"];
alert( arr.length ); // 3

Eleman silinmesine rağmen, dizi hala 3 elemana sahip. Bunu arr.length == 3 kontrolünü yaparak görebilirsiniz.

Olması gereken de bu, çünkü delete obj.key değeri anahtara göre siler. Sadece bu işi yapar. Bu da objeler için yeterlidir. Fakat diziler için genelde istediğimiz elamanların birbirlerinin yerleri doldurmasıdır. Bundan dolayı dizinin kısaldığını görmemiz lazım.

Bundan dolayı özel metodlar kullanılmalıdır.

arr.splice(str) metodu isviçre çakısı gibi her işe yarar. Diziye yeni bir eleman ekleyebilir ve silebilir.

Yazımı:

arr.splice(index[, deleteCount, elem1, ..., elemN])

index’ten başlar ve deleteCountkadar elemanı siler ve sonra elem1, ..., elemN şeklinde yerlerine yerleştirir. Diziden silinen elemanları dönderir.

Bu metodu örnek ile anlamak çok daha kolaydır.

Silme işlemi ile başlayalım:

let arr = ["Ben", "JavaScript", "çalışıyorum"];

arr.splice(1, 1); // index 1'den 1 elaman sil

alert( arr ); // ["Ben", "çalışıyorum"]

Kolay değil mi? 1. indeksten başlayarak 1 eleman sildi.

Bir sonraki örnekte ise 4 eleman silinecek ve yeni bir eleman bunların yerine konulacak.

let arr = ["Ben", "şu", "an", "JavaScript", "çalışıyorum"];

// İlk 4 elamanı sil ve öncesine yeni eleman ekle.
arr.splice(0, 4, "Ders")

alert( arr ) // Şimdi [Ders çalışıyorum]

Burada splice in silinen elemanları döndürdüğü görülebilir.

let arr = ["Ben", "şu", "an", "JavaScript", "çalışıyorum"];

// ilk iki elemanı sil.
let removed = arr.splice(0, 2);

alert( removed ); // "Ben", "şu" <-- silinen elemanlar

splice metodu ayrıca hiçbir şey silmeden de ekleme yapabilir. Bunun için deleteCount’u 0 yapmanız gerekmektedir:

let arr = ["Ben", "JavaScript", "çalışıyorum"];

// 1. indeksten
// 0 tane sil
// Sonrasında "karmaşık" ekle
arr.splice(1, 0,  "karmaşık");

alert( arr ); // "Ben", "karmaşık", "JavaScript", "çalışıyorum"
Negatif indeksler de kullanılabilir

Bu ve diğer metodlarda negatif indeksler kullanılabilir. Negatif indeksler dizinin sonundan başına doğrudur. Örneğin:

let arr = [1, 2, 5]

// indeks -1 ( sondan birinci )
// 0 eleman sil,
// 3 vs 4 ekle
arr.splice(-1, 0, 3, 4);

alert( arr ); // 1,2,3,4,5

slice

arr.slice metodu arr.splice’a göre daha basittir.

Yazımı:

arr.slice(start, end)

Yeni bir dizi döndürür. Bu dizi içerisinde "start" ile "end" arasında ( "end" dahil olmadan ) tüm elemanları kopyalar. start ve end negatif olabilir. Negatif durumlarda dizi sondan değer başlar.

str.slice gibi çalışır fakat karakter dizisi(string) yapmak yerine alt-dizi yapar.

Örneğin:

let str = "test";
let arr = ["t", "e", "s", "t"];

alert( str.slice(1, 3) ); // es
alert( arr.slice(1, 3) ); // e,s

alert( str.slice(-2) ); // st
alert( arr.slice(-2) ); // s,t

concat

arr.concat metodu dizi ile diğer dizileri veya elemanları birbirine eklemeye yarar.

Yazımı:

arr.concat(arg1, arg2...)

İstenildiği kadar argümanı kabul eder, bunlar dizi veya değer olabilir.

Sonuç arr, ardından arg1, arg2 şeklinde tüm dizileri ve değerleri içeren bir dizi olur.

Eğer bir argüman dizi ve Symbol.isConcatSpreadable özelliğine sahip ise bunun tüm alt elemanları kopyalanır. Diğer türlü argümanın sadece kendisi kopyalanır.

Örneğin:

let arr = [1, 2];

// diziyi [3,4] ile birleştir
alert( arr.concat([3, 4])); // 1,2,3,4

// diziyi [3,4] ve [5,6] ile birleştir
alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6

// diziyi [3,4] ile birleştir ve ardından 5, 6 ekle
alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6

Normalde, dizide bulunan elemanları kopyalar. Diğer objeler dizi olsalar bile bir bütün olarak eklenirler.

let arr = [1, 2];

let arrayLike = {
  0: "something",
  length: 1
};

alert( arr.concat(arrayLike) ); // 1,2,[object Object]
//[1, 2, arrayLike]

…Fakat dizi benzeri obje Symbol.isConcatSpreadable özelliğine sahipse, bunların elemanları eklenir:

let arr = [1, 2];

let arrayLike = {
  0: "başka",
  1: "bir şey",
  [Symbol.isConcatSpreadable]: true,
  length: 2
};

alert( arr.concat(arrayLike) ); // 1,2,başka,bir şey

Dizide arama

Dizi içerisinde aramak için bazı metodlar bulunmaktadır.

indexOf/lastIndexOf ve includes

arr.indexOf, arr.lastIndexOf ve arr.includes aynı yazıma sahiptirler, ve aslında hepsi aynı işi yapar. Sadece karakterler yerine elemanlar üzerinde çalışırlar.

  • arr.indexOf(eleman, balangic) baslangic indeksinden itibaren eleman’ı arar ve bulursa bunun indeksini döner, bulamazsa -1 döner.
  • arr.lastIndexOf(eleman, baslangic) – aynı, fakat bu sağdan sola doğru bakar.
  • arr.includes(eleman, baslangic)eleman baslangıc’tan başlayarak elemanları kontrol eder. Bulursa true döner.

Örneğin:

let arr = [1, 0, false];

alert( arr.indexOf(0) ); // 1
alert( arr.indexOf(false) ); // 2
alert( arr.indexOf(null) ); // -1

alert( arr.includes(1) ); // true

Bu metodlar eşitlik kontrolü için === kullanır. Bundan dolayı false’a bakacak olursanız 0 ile eşit değildir. Sadece false ile eşittir. Eğer sadece dizi içinde var olup olmadığını kontrol etmek istiyorsanız arr.includes tercih edilir.

find and findIndex

Objelerden oluşma bir dizinin olduğunu varsayın. Bazı şartları sağlayan objeleri nasıl bulursunuz.

Burada arr.find metodu yararlı olur.

Yazımı:

let result = arr.find(function(elaman, index, dizi) {
  //  eğer aranan eleman bulunursa true döndürmeli.
});

Bu fonksiyon her eleman için tekrar tekrar çağırılır.

  • elaman eleman’ı tanımlar.
  • index indeks’i tanımlar.
  • array dizinin kendisidir.

Eğer true döndürür ise arama durur ve eleman’ın kendisi döner. Eğer bulunamazsa undefined döndürülür.

Örneğin, kullanıcıların bulunduğu bir dizi ve her dizide id ve isim alanları bulunsun. id == 1 olan elemanı bulalım.

let kullanicilar = [
  {id: 1, isim: "Ahmet"},
  {id: 2, isim: "Muzaffer"},
  {id: 3, isim: "Emine"}
];

let kullanici = kullanicilar.find(eleman => eleman.id == 1);

alert(kullanici.isim); // Ahmet

Objelerin dizi içerisinde yer alması çokça karşılaşılan bir olaydır, bundan dolayı find metodu çok kullanışlıdır.

Dikkat ederseniz find metodunda sadece bir tane argüman kullanılmıştır item => item.id == 1. find metodunun diğer parametreleri çok nadir olarak kullanılır.

arr.findIndex metodu da aynı find metodu gibi çalışır fakat elemanın kendi yerine index’ini döndürür.

filter

find metodu sadece fonksiyonu true yapan elemana bakar.

Birden fazlası için ise arr.filter(fn) kullanılabilir.

Yazımı neredeyse find ile aynıdır, fakat tek bir eleman yerine kurala uyan elemanları dizi halinde döner.

let results = arr.filter(function(eleman, index, dizi) {
  // eğer elemanlar filtreye uygunsa true döndürür.
});

Örneğin:

let kullanicilar = [
  {id: 1, isim: "Ahmet"},
  {id: 2, isim: "Muzaffer"},
  {id: 3, isim: "Emine"}
];

// ilk iki kullaniciyi döndürür.
let baziKullanicilar = kullanicilar.filter(eleman => eleman.id < 3);

alert(baziKullanicilar.length); // 2

Dizi dönüşümleri

Bu bölüm dizinin dönüşümleri veya yeniden sıralanması hakkındadır.

map

arr.map metodu en fazla kullanılan ve kullanışlı olan metodlardandır.

Yazımı:

let sonuc = arr.map(function(eleman, index, dizi) {
  // eleman yerine yeni değer döndürür.
})

Dizinin her elemanı için fonksiyonu çalıştırır ve sonuçlarını dizi olarak döner.

Örneğin elemanların uzunlukları ile ilgili bir değişiklik yapılabilir:

let uzunluklar = ["Bilbo", "Gandalf", "Nazgul"].map(eleman => eleman.length)
alert(uzunluklar); // 5,7,6

sort(fn)

arr.sort metodu diziyi olduğu yerde sıralar.

Örneğin:

let arr = [ 1, 2, 15 ];

// metod dizinin içeriğini sıralar ve döndürür.
arr.sort();

alert( arr );  // 1, 15, 2

Çıktısında bir şey fark ettiniz mi?

Sıralama 1, 15, 2 oldu. Yanlış. Neden peki?

Diziler varsayılan olarak karakter sıralamasına göre sıralanırlar.

Tüm elemanlar karakter dizisine çevrilir ve karşılaştırılır. Bundan dolayı karakter sırasına göre "2" > "15" karşılaştırılır.

Kendi sıralamanızı yapmak için, iki argümanlı bir fonksiyonu arr.sort()'ın argüman olarak alması gerekmektedir.

Fonksiyon aşağıdaki şekilde çalışmalıdır:

function compare(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

Örneğin:

function compareNumeric(a, b) {
  if (a > b) return 1;
  if (a == b) return 0;
  if (a < b) return -1;
}

let arr = [ 1, 2, 15 ];

arr.sort(compareNumeric);

alert(arr);  // 1, 2, 15

Şimdi beklendiği gibi çalışmakta.

Ne olduğunu düşünürsek. arr her şeyi tutabilir, değil mi? Sayı, karakter veya html elementi vs. tutabilir. İçinde bulunanları sıralamak için karşılaştırmayı yapan sıralama fonksiyonuna ihtiyaç vardır. Bunun da varsayılanı karakter sıralamadır.

arr.sort(fn) metodu içinde sıralama algoritmasına sahiptir. Bu sıralamanın nasıl çalıştığına dair bir bilgimiz olmasına gerek yok (Çoğu zaman quicksort kullanılır). Diziyi dolanır ve elemanları verilen algoritmaya göre karşılaştırır ve sıralar. Tek bilmeniz gereken fn fonksiyonunun karşılaştırmayı yaptığıdır.

Eğer hangi elemanın karşılaştırıldığını öğrenmek istiyorsanız elbette bunu görebilirsiniz.

[1, -2, 15, 2, 0, 8].sort(function(a, b) {
  alert( a + " <> " + b );
});

Algoritma aynı elemanı bir kaç defa çalıştırma ihtiyacı duyabilir, fakat yine de olduğunca az karşılaştırmaya çalışır.

Karşılaştırma fonksiyonu herhangi bir sayıyı döndürebilir.

Aslında, karşılaştırma fonksiyonu “büyük” olduğunu belirtmek için pozisitif sayı, “az” olduğunu belirtmek için negatif sayı döndürmelidir.

Bu daha kısa fonksiyon yazılmasına olanak sağlar:

let arr = [ 1, 2, 15 ];

arr.sort(function(a, b) { return a - b; });

alert(arr);  // 1, 2, 15
Daha zarif bir fonksiyon için ok kullanmak.

Ok fonksiyonlarını hatırlarsanız burada daha zarif bir biçimde sıralama yapılabilir:

arr.sort( (a, b) => a - b );

Bu, daha uzun versiyonu ile aynı şekilde çalışır.

reverse

arr.reverse metodu arr’in sıralamasını terse çevirir.

Örneğin:

let arr = [1, 2, 3, 4, 5];
arr.reverse();

alert( arr ); // 5,4,3,2,1

Ayrıca terse çevirmesinden sonra arr’i döndürür.

split and join

Bunun uygulamasını gerçek hayatta şu şekilde görmek mümkndür. Bir mesajlaşma uygulaması yazıdğınızı düşünün. Gönderen kişi alıcıları virgülle ayırarak yazsın. Örneğin Ahmet, Mehmet, Muzaffer gibi. Fakat bizim için bunun bir dizi olması karakter dizisi olmasından daha kullanışlıdır. Peki bu nasıl yapılmalı?

str.split(delim) tam olarak bunu yapmaktadır. Karakterleri verilen delim e göre ayırır ve sonrasında bunları dizi olarak döner.

Aşağıdaki örnekte isimler virgül ve ardından boşluk yazarak ayrılmıştır.

let isimler = 'Bilbo, Gandalf, Nazgul';

let arr = isimler.split(', ');

for (let isim of arr) {
  alert( ` ${name}'e mesaj.` ); // Bilbo'e mesaj ve diğerleri.
}

split metodu isteğe bağlı ikincil bir sayısal argüman alabilir – dizinin boyutu. Eğer bu verilirse, dizi bu verilen uzunluk kadar dolduktan sonra geri kalanlar görmezden gelinir. Pratikte çok nadir kullanılır.

let dizi = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);

alert(arr); // Bilbo, Gandalf
Harflere Çevirme

split(s)i boş s ile çağırırsanız harflerin dizisi haline getirirsiniz.

let str = "test";

alert( str.split('') ); // t,e,s,t

arr.join(str) split in tam tersini yapar. arr’den karakter dizileri yaratır.

Örnek:

let dizi = ['Bilbo', 'Gandalf', 'Nazgul'];

let str = dizi.join(';');

alert( str ); // Bilbo;Gandalf;Nazgul

reduce/reduceRight

Dizi elemanlarının üzerinden geçilmek istendiğinde forEach kullanmak mümkündür.

arr.reduce ve arr.reduceRight metodları da bu işe yarar fakat daha dallı budaklıdır. Genelde dizilere göre tek bir karakter dizisini hesaplamaya yarar.

Yazımı:

let value = arr.reduce(function(previousValue, item, index, arr) {
  // ...
}, initial);

Fonksiyon elemanlara uygulanır. İkinciden itibaren benzer bir yazıma rastlayabilirsiniz.

  • item – dizinin o anki elemanı.
  • index – elemanın pozisyonu.
  • arr – dizi.

Şimdiye kadar forEach/map gibi. Fakat bir argüman daha var:

  • previousValue bir önceki fonksiyonun sonucudur initial ilk çağrının sonucudur.

Örnekle anlatmak gerekirse:

Aşağıda dizinin toplamı bir satırda alınmaktadır:

let arr = [1, 2, 3, 4, 5]

let result = arr.reduce((sum, current) => sum + current, 0);

alert(result); // 15

Burada reduce fonksiyonunun en çok kullanılan 2 argümanlı şekli kullanıldı.

Detaylarına bakılacak olursa:

  1. İlk çalıştırıldığında sum başlangıç değerini alır ( reduce’un son argümanı ) 0, ve current dizinin ilk elemanıdır 1. Bundan dolayı sonuç 1 olur.
  2. İkinci döngüde sum = 1, buna ikinci dizi elemanı olan 2 eklenir ve döndürülür.
  3. Üçüncü döngüde ise sum = 3 ve buna bir sonraki dizi elemanı eklenir ve böyle devam eder.

Hesaplama akışı:

Form tablosunda bunu daha açık bir şekilde görebilirsiniz. Satırlar fonksiyon çağrılarını göstermektedir.

toplam şimdiki sonuç
birinci çağrı 0 1 1
ikinci çağrı 1 2 3
üçüncü çağrı 3 3 6
dördüncü çağrı 6 4 10
beşinci çağrı 10 5 15

Gördüğünüz gibi bir önceki fonksiyonun sonucu sonraki fonksiyonun argümanı olmakta.

Bunun ile birlikte başlangıç değerini pas geçmekte mümkün:

let arr = [1, 2, 3, 4, 5];

// başlangıç değeri silindi, 0 değil.
let result = arr.reduce((sum, current) => sum + current);

alert( result ); // 15

Sonuç aynı. Başlangıç değeri olmadığından dolayı, reduce fonksiyonu diznin ilk elemanını başlangıç değeri olarak almakta ve ikinciden itibaren döngüye başlamaktadır.

Hesaplama tablosu yukarıdaki ile aynı olmaktadır, sadece birinci satır silinir.

Fakat böyle kullanımda çok dikkatli olunmalıdır. Eğer dizi boş ise, recude çağrısı başlangıç değeri olmadığında hata verir.

Örneğin:

let arr = [];

// Hata: Başlangıç değeri olmayan boş dizi ile `reduce` fonksiyonu kullanıldı.
// Eğer başlangıç değeri olsaydı, `reduce` boş diziyi döndürebilirdi.
arr.reduce((sum, current) => sum + current);

Bundan dolayı her zaman başlangıç değeri kullanılması önerilir.

arr.reduceRight metodu da reduce metodu ile aynı işi yapar fakat diziyi sağdan sola doğru okur.

Tekrar: forEach

arr.forEach metodu her eleman için bir fonksiyon çalıştırmaya yarar.

Yazımı:

arr.forEach(function(item, index, array) {
  // ... elemanla bir şeyler yap
});

Örneğin aşağıdaki kod dizinin her elemanını göstermeye yarar:

// her eleman için alert çağır
["Bilbo", "Gandalf", "Nazgul"].forEach(alert);

Aşağıdaki kod elemanın dizideki pozisyonu hakkında daha açıklayıcıdır:

["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
  alert(`${item} ${array}'in ${index}. indeksinde`);
});

Eğer fonksiyonun bir sonucu varsa bu görmezden gelinir.

Array.isArray

Diziler farklı bir tip değildir. Obje üzerine kurulmuşlardır.

Bundan dolayı typeof normal obje ile diziyi ayırt etmekte yardımcı olamaz:

alert(typeof {}); // object
alert(typeof []); // aynısı

… Fakat diziler çok kullanıldığından dolayı buna has metod bulunmaktadır: Array.isArray(value) . Eğer değer dizi ise true döndürür. Diğer türlü false döndürür.

alert(Array.isArray({})); // false

alert(Array.isArray([])); // true

Çoğu metod “thisArg”'ı destekler

Fonksiyonları çağıran neredeyse tüm dizi metodları – find, filter, map gibi sort hariç, ayrıca opsiyonel thisArg parametresini kabul eder.

Yukarıdaki bölümde bu parametreden bahsedilmedi, bunun sebebi çok nadir olarak kullanılmasından dolayıdır. Fakat bütünlüğün sağlanmasından dolayı üstünden geçmekte fayda var.

Bu metodlarla “thisArg”'ın yazımı aşağıdaki gibidir:

arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
// ...
// thisArg isteğe bağlı olarak kullanılan bir argümandır.

thisArg değeri func için this olmaktadır.

Örneğin, aşağıda objenin metodu filtre olarak kullanılmaktadır ve thisArg bu durumda oldukça kullanışlıdır:

let kullanici = {
  yas: 18,
  dahaGenc(digerKullanici) {
    return digerKullanici.yas < this.yas;
  }
};

let kullanicilar = [
  {yas: 12},
  {yas: 16},
  {yas: 32}
];

// kullanıcıdan daha genç kullanıcıları bulunuz
let dahaGencKullanicilar = kullanicilar.filter(kullanici.dahaGenc, kullanici);

alert(dahaGencKullanicilar.length); // 2

Yukarıdaki çağrıda kullanici.dahaGenc filtre olarak kullanılmaktadır. Ayrıca kullanici bu fonksiyona gönderilmektedir. Eğer kullanici.filter(kullanici.dahaGenc)'i vermezseniz, kullanici.dahaGenc this=undefined olarak çağrılır. Bu da anında hata verir.

Özet

Dizi metodlarının kısa açıklamaları:

  • Eleman ekleme/silme metodları:

    • push(...items) – elemanları sona ekler,
    • pop() – en sondaki elemanı alır,
    • shift() – başlangıçtan eleman alır,
    • unshift(...items) – başlangıça eleman ekler
    • splice(pos, deleteCount, ...items)pos indeksinde deleteCount sayısı kadar elemanı siler ve bunları items’a ekler.
    • slice(start, end)start ile end pozisyonları arasındaki (end dahil değil) elemanları yeni bir diziye kopyalar.
    • concat(...items) – yeni bir dizi döndürür: var olan dizideki tüm elemanları kopyalar ve items’ı ekler. Eğer items dizi ise bunun elemanları da alınır.
  • Elemanları aramaya yönelik metodlar:

    • indexOf/lastIndexOf(item, pos)pos’tan başlayarak item’ı arar. Bulursa indeksini döndürür, bulamaz ise -1 döndürür.
    • includes(value) – eğer dizi value’ya sahipse true döndürür. Diğer türlü false döndürür.
    • find/filter(func) – Elemanları fonksiyonlar ile filtreler. Buna göre fonksiyonu true yapan ilk veya tamamını döner.
    • findIndex aynı find gibidir fakat bir değer yerine index döner.
  • Diziler üzerinde dönüşümler:

    • map(func) – her eleman için func çağrılır ve bunların sonuçlarından bir dizi üretilerek döndürülür.
    • sort(func) – diziyi olduğu yerde sıralar ve döndürür.
    • reverse() – diziyi terse çevirir ve döndürür.
    • split/join – karakterleri diziye çevirir veya dizileri karaktere çevirir.
    • reduce(func, initial) – dizide bulunan elemanlar sıra ile func fonksiyonu üzerinden hesaplanır ve son değer döndürülür.
  • Elemanlar üzerinden dönme:

    • forEach(func) – dizide bulunan her eleman için func çağrılır. hiçbir şey döndürmez.
  • Ek olarak:

    • Array.isArray(arr) arr’in dizi olup olmadığını kontrol eder.

Bu metodların içinden sadece sort, reverse ve splice doğrudan dizinin kendisi üzerinden işlem yapar. Diğerleri değer döndürür.

Yukarıdaki metodlar projelerin çoğundaki kullanılan dizi fonksiyonlarının %99’unu kapsar. Fakat bunun yanında farklı metodlar da bulunmaktadır:

  • arr.some(fn)/arr.every(fn) diziyi kontrol eder.

    Dizinin her elemanı için fn çağırılır. Map’e çok benzer fakat herhangi biri/hepsi true ise true döndürür. Diğer türlü false döndürür.

  • arr.fill(value, start, end) – diziyi tekrar eden value değeri ile start ile index arasına doldurur.

  • arr.copyWithin(target, start, end)start tan end’e kadar olan elemanları target’tan itibaren var olanların üzerine yazarak yapıştırır.

Tüm liste için kullanım talimatları sayfasına bakabilirsiniz.

Görünürde çok fazla metod varmış gibi ve ezberlemesi zormuş gibi görünse de aslında göründüğünden çok daha kolaydır.

Sadece tanımların bulunduğu sayfaya bakmanız yeterlid. Ardından bu bölümdeki örnekleri çözerek pratik yaparsanız metodlar ile ilgili yeteri kadar bilgi sahibi olmuş olursunuz.

Daha sonrasında metodlar ile ilgili bir şey yapmak istediğinizde, nasıl yapıldığını bilmiyorsanız, buraya tekrar gelip doğru metodu bulabilirsiniz. Buradaki örnekler doğru bir şekilde yazmanıza yardımcı olacaktır. Sonrasında metodları hiçbir özel çaba harcamadan hatırlayacak duruma gelebilirsiniz.

Görevler

önem: 5

camelize(str) fonksiyonu yazınız. Bu metod “benim-öz-geçmişim” gibi yazılan kelimeleri “benimÖzGeçmişim” şekline getiren fonksiyonu yazınız.

Bu fonksiyon: tüm tireleri silmeli, dashten sonraki kelimenin ilk harfi büyük harf haline getirilmeli.

Örnek:

camelize("background-color") == 'backgroundColor';
camelize("list-style-image") == 'listStyleImage';
camelize("-webkit-transition") == 'WebkitTransition';

Not: split kullanarak karakterleri dizi haline getirebilirsiniz, bunu join ile tekrar karakter dizisi haline getirin.

Testler ile korunaklı olan aç.

function camelize(str) {
  return str
    .split('-') // splits 'my-long-word' into array ['my', 'long', 'word']
    .map(
      // capitalizes first letters of all array items except the first one
      // converts ['my', 'long', 'word'] into ['my', 'Long', 'Word']
      (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
    )
    .join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord'
}

Çözümü testler korunaklı alanda olacak şekilde aç.

önem: 4

filterRange(arr, a, b) adında bir fonksiyon yazın. arr argümanı alsın, a ile b arasını alsın ve döndersin.

Fonksiyon diziyi modifiye etmemeli. Yeni bir dizi döndürmeli.

Örneğin:

let arr = [5, 3, 8, 1];

let filtered = filterRange(arr, 1, 4);

alert( filtered ); // 3,1 (eşleşen değerler)

alert( arr ); // 5,3,8,1 (modifiye edilmedi)

Testler ile korunaklı olan aç.

function filterRange(arr, a, b) {
  // added brackets around the expression for better readability
  return arr.filter(item => (a <= item && item <= b));
}

let arr = [5, 3, 8, 1];

let filtered = filterRange(arr, 1, 4);

alert( filtered ); // 3,1 (matching values)

alert( arr ); // 5,3,8,1 (not modified)

Çözümü testler korunaklı alanda olacak şekilde aç.

önem: 4

arr alan ve filterRangeInPlace(arr,a,b) fonksiyonu ile a ile b arasındaki değerleri alıp ve bunun haricindekileri bu diziden silen fonksiyonu yazınız.

Fonksiyon diziyi modifiye etmeli. Bir şey döndürmemeli.

Örneğin:

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4); // 1 ile 4 aralığı dışındaki sayıları siliniz.

alert( arr ); // [3, 1]

Testler ile korunaklı olan aç.

function filterRangeInPlace(arr, a, b) {

  for (let i = 0; i < arr.length; i++) {
    let val = arr[i];

    // remove if outside of the interval
    if (val < a || val > b) {
      arr.splice(i, 1);
      i--;
    }
  }

}

let arr = [5, 3, 8, 1];

filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4

alert( arr ); // [3, 1]

Çözümü testler korunaklı alanda olacak şekilde aç.

önem: 4
let arr = [5, 2, 1, -10, 8];

// ... kodunuz bu diziyi tersten sıralamalıdır.

alert( arr ); // 8, 5, 2, 1, -10
let arr = [5, 2, 1, -10, 8];

arr.sort((a, b) => b - a);

alert( arr );
önem: 5

Karakterlerden oluşan bir arr disini, kopyalayan ve sıralayan fakat arr de bir değişikliğe neden olmayan fonksiyonu yazınız.

Bu kopyayı döndüren copySorted(arr) fonksiyonunu yazınız.

let arr = ["HTML", "JavaScript", "CSS"];

let sorted = copySorted(arr);

alert( sorted ); // CSS, HTML, JavaScript
alert( arr ); // HTML, JavaScript, CSS (değişmedi)

slice() fonksiyonunu kullanarak bir kopyasını yaratabilir ve sonrasında bunu sıralayabilirsiniz:

function copySorted(arr) {
  return arr.slice().sort();
}

let arr = ["HTML", "JavaScript", "CSS"];

let sorted = copySorted(arr);

alert( sorted );
alert( arr );
önem: 5

Create a constructor function Calculator that creates “extendable” calculator objects.

The task consists of two parts.

  1. First, implement the method calculate(str) that takes a string like "1 + 2" in the format “NUMBER operator NUMBER” (space-delimited) and returns the result. Should understand plus + and minus -.

    Usage example:

    let calc = new Calculator;
    
    alert( calc.calculate("3 + 7") ); // 10
  2. Then add the method addMethod(name, func) that teaches the calculator a new operation. It takes the operator name and the two-argument function func(a,b) that implements it.

    For instance, let’s add the multiplication *, division / and power **:

    let powerCalc = new Calculator;
    powerCalc.addMethod("*", (a, b) => a * b);
    powerCalc.addMethod("/", (a, b) => a / b);
    powerCalc.addMethod("**", (a, b) => a ** b);
    
    let result = powerCalc.calculate("2 ** 3");
    alert( result ); // 8
  • No brackets or complex expressions in this task.
  • The numbers and the operator are delimited with exactly one space.
  • There may be error handling if you’d like to add it.

Testler ile korunaklı olan aç.

  • Please note how methods are stored. They are simply added to the internal object.
  • All tests and numeric conversions are done in the calculate method. In future it may be extended to support more complex expressions.
function Calculator() {

  let methods = {
    "-": (a, b) => a - b,
    "+": (a, b) => a + b
  };

  this.calculate = function(str) {

    let split = str.split(' '),
      a = +split[0],
      op = split[1],
      b = +split[2]

    if (!methods[op] || isNaN(a) || isNaN(b)) {
      return NaN;
    }

    return methods[op](a, b);
  }

  this.addMethod = function(name, func) {
    methods[name] = func;
  };
}

Çözümü testler korunaklı alanda olacak şekilde aç.

önem: 5

kullanici objelerinden oluşan bir dizinin kullanici.adi özelliğinden dizi yapan kodu yazınız.

Örneğin:

let ahmet = { adi: "Ahmet", yas: 25 };
let mehmet = { adi: "Mehmet", yas: 30 };
let muzaffer = { adi: "Muzaffer", yas: 28 };

let kullanici = [ ahmet, mehmet, muzaffer ];

let isimler = /* ... kodunuz */

alert( isimler ); // Ahmet, Mehmet, Muzaffer
let ahmet = { adi: "Ahmet", yas: 25 };
let mehmet = { adi: "Mehmet", yas: 30 };
let muzaffer = { adi: "Muzaffer", yas: 28 };

let kullanici = [ ahmet, mehmet, muzaffer ];

let isimler = kullanici.map(eleman => eleman.adi);

alert( isimler ); //  Ahmet, Mehmet, Muzaffer
önem: 5

kullanici dizindeki elemanlar adi, soyadi ve id özelliklerine sahiptirler.

Bunlardan özellikleri id, adi_soyadi şeklinde adi ve soyadi özelliklerinden türeyen adi_soyadi özelliğine sahip objelerden sahip yeni bir dizi oluşturunuz.

Örneğin:

let ahmet = { adi: "Ahmet", soyadi: "Doğtaş", id: 1 };
let mehmet = { adi: "Mehmet", soyadi: "İstikbal", id: 2 };
let muzaffer = { adi: "Muzaffer", soyadi: "Bellona", id: 3 };

let kullanicilar = [ ahmet, mehmet, muzaffer ];

let kullaniciMapped = /* ... Sizin kodunuz ... */

/*
kullaniciMapped = [
  { adi_soyadi: "Ahmet Doğtaş", id: 1 },
  { adi_soyadi: "Mehmet İstikbal", id: 2 },
  { adi_soyadi: "Muzaffer Bellona", id: 3 }
]
*/

alert( kullaniciMapped[0].id ) // 1
alert( kullaniciMapped[0].adi_soyadi ) // Ahmet Doğtaş

Burada yapmanız gereken aslında bir dizideki objeleri diğerine eşlemek(map etmek). => kullanabilirsiniz.

let ahmet = { adi: "Ahmet", soyadi: "Doğtaş", id: 1 };
let mehmet = { adi: "Mehmet", soyadi: "İstikbal", id: 2 };
let muzaffer = { adi: "Muzaffer", soyadi: "Bellona", id: 3 };

let kullanicilar = [ ahmet, mehmet, muzaffer ];

let kullaniciMapped = kullanicilar.map(kullanici => ({
  adi_soyadi: `${kullanici.adi} ${kullanici.soyadi}`,
  id: kullanici.id
}));

/*
kullaniciMapped = [
  { adi_soyadi: "Ahmet Doğtaş", id: 1 },
  { adi_soyadi: "Mehmet İstikbal", id: 2 },
  { adi_soyadi: "Muzaffer Bellona", id: 3 }
]
*/

alert( kullaniciMapped[0].id ) // 1
alert( kullaniciMapped[0].adi_soyadi ) // Ahmet Doğtaş

Dikkat ederseniz ok fonksiyonunda süslü parantez kullanmamız gerekti.

Aşağıdaki gibi yazılamaz:

let kullaniciMapped = kullanicilar.map(kullanici => {
  adi_soyadi: `${kullanici.adi} ${kullanici.soyadi}`,
  id: kullanici.id
});

Hatırlayacağınız üzere iki türlü ok fonksiyonu bulunmaktadır: Gövdesi olmadan deger => ifade veya gövdeli deger => {...}

Bizim kullandığımız şekliyle JavaScript {'i fonksiyon başlangıcı olarak kabul etmektedir. Objenin başlangıcı değil. Halbuki biz obje olmasını istiyoruz. Bu durumda bunları “normal” parantez içine almamız gerekmekte.

let kullaniciMapped = kullanicilar.map(kullanici => ({
  adi_soyadi: `${kullanici.adi} ${kullanici.soyadi}`,
  id: kullanici.id
}));

Şimdi çalışır.

name özelliğine sahip obje dizisini alan ve sortByName(kullanicilar) sıralayan fonksiyonu yazınız.

Örneğin:

let muzaffer = { adi: "Muzaffer", yas: 25 };
let mehmet = { adi: "Mehmet",yas: 30 };
let ahmet = { adi: "Ahmet", yas: 28 };


let arr = [   muzaffer , mehmet, ahmet ];

sortByName(arr);

// şimdi: [ahmet, mehmet, muzaffer]
alert(arr[1].adi) // Mehmet
function sortByName(arr) {
  arr.sort((a, b) => a.adi > b.adi);
}

let muzaffer = { adi: "Muzaffer", yas: 25 };
let mehmet = { adi: "Mehmet",yas: 30 };
let ahmet = { adi: "Ahmet", yas: 28 };

let arr = [   muzaffer , mehmet, ahmet ];

sortByName(arr);

// şimdi: [ahmet, mehmet, muzaffer]
alert(arr[1].adi) // Mehmet
önem: 3

karistir(dizi) adında bir fonksiyon yazın ve bu fonksiyon dizideki elemanları her defasında rasgele yeniden dizsin.

let arr = [1, 2, 3];

shuffle(arr);
// arr = [3, 2, 1]

shuffle(arr);
// arr = [2, 1, 3]

shuffle(arr);
// arr = [3, 1, 2]
// ...

Her eleman aynı olasılıkla sıralanmalıdır. Örneğin [1,2,3] [1,2,3] veya [1,3,2] veya [3,1,2] vs. şeklinde sıralanabilir olmalıdır.

En basit çözümü aşağıdaki gibi olabilir.

function karistir(dizi) {
  dizi.sort(() => Math.random() - 0.5);
}

let dizi = [1, 2, 3];
karistir(dizi);
alert(dizi);

Yukarıdaki çalışıyor denebilir çünkü Math.random()-0.5 rasgele bir sayıdır ve pozitif veya negatif olabilir. Böylece sıralama fonksiyonu rasgele elemanları dizer.

Fakat sıralama fonksiyonu bu amaçla kullanılamaz. Tüm permütasyon aynı olasılıkta değildirler.

Aşağıdaki koda bakılacak olursa karistir 1000000 defa çalıştırılacak olursa bile olası sonuçlar şu şekildedir:

function shuffle(array) {
  array.sort(() => Math.random() - 0.5);
}

// mümkün olan tüm permütasyonların görünme sayısı.
let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for(let i = 0; i < 1000000; i++) {
  let array = [1, 2, 3];
  karistir(array);
  count[array.join('')]++;
}

// mümkün olan tüm permütasyonların görünme sayısı.
for(let key in count) {
  alert(`${key}: ${count[key]}`);
}

( Temmuz 2017 itibari ile sonuçlar aşağıdaki gibidir.)

123: 250706
132: 124425
213: 249618
231: 124880
312: 125148
321: 125223

Görüğünüz gibi 123 ve 213 ün çıkma olasılığı daha fazladır.

Sonuçlar JavaScript motoruna göre değişebilir. Fakat görüldüğü gibi bu fonksiyon çok güvenilir değildir.

Neden çalışmadı? Genel olarak konuşmak gerekirse sort kara kutudur: biz buraya bir dizi göndeririz o içinde karşılaştırma fonksiyonları vs. kullanır ve biz sıralanmış şekilde bu diziyi alırız. Bu kadar fazla rasgele karşılaştırmadan dolayı bu kara kutu deliye döndü, bu deliye dönme olayında nasıl davranacağı da JavaScript motoruna bağlıdır.

Bu problem bir kaç yöntemle çözülebilir. Fisher-Yates shuffle. Fikre göre dizi tersten başlayarak rasgele sayı ile değiştirilecek şekilde yazılmalıdır:

function karistir(array) {
  for(let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i+1)); // random index  0 ile i arasında
    [array[i], array[j]] = [array[j], array[i]]; // elemanların yer değiştirmesi için.
  }
}

Aynı şekilde test edilirse:

function karistir(array) {
  for(let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i+1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

// mümkün olan tüm permütasyonların görünme sayısı.
let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for(let i = 0; i < 1000000; i++) {
  let array = [1, 2, 3];
  karistir(array);
  count[array.join('')]++;
}

// show counts of all possible permutations
for(let key in count) {
  alert(`${key}: ${count[key]}`);
}

The example output:

123: 166693
132: 166647
213: 166628
231: 167517
312: 166199
321: 166316

Şimdi daha iyi görünüyor: tüm permütasyonlar yakın olasılıkla.

Performans yönünden Fisher-Yates algoritması harikatıdır. Hiç sıralama ile uğraşmaya gerek yok.

önem: 4

ortalamaYasAl(kullanicilar) adında bir fonksiyon alınız. Bu fonksiyon yas ozelliğine sahip objelerden oluşan bir dizi alsın ve bunların ortalamasını hesaplasın.

Ortalama formülü (yas1 + yas2 + ..... + yasN) / N

Örneğin:

let muzaffer = { adi: "Muzaffer", yas: 25 };
let mehmet = { adi: "Mehmet",yas: 30 };
let ahmet = { adi: "Ahmet", yas: 29 };

let arr = [   muzaffer , mehmet, ahmet ];

alert( ortalamaYasAl(arr) ); // (25+30+29)/3 = 28
function ortalamaYasAl(kullanicilar) {
  return kullanicilar.reduce((onceki, kullanici) => onceki + kullanici.yas, 0) / kullanicilar.length;
}

let muzaffer = { adi: "Muzaffer", yas: 25 };
let mehmet = { adi: "Mehmet",yas: 30 };
let ahmet = { adi: "Ahmet", yas: 29 };

let arr = [   muzaffer , mehmet, ahmet ];

alert( ortalamaYasAl(arr) ); // 28
önem: 4

Diyelim ki arr adında bir diziniz var.

benzersiz(arr) adında bir fonksiyon yazın ve bu fonksiyon arr içinde bulunan benzersiz elemanları dönsün.

Örneğin:

function benzersiz(arr) {
  /* your code */
}

let kullanicilar = ["Emine", "Muzaffer", "Fatma", "Kanako",
  "Kanako", "Muzaffer", "Fatma", "Kanako", ":-O"
];

alert( benzersiz(kullanicilar) ); // Emine, Muzaffer, Fatma, Kanako, :-O

Testler ile korunaklı olan aç.

Dizinin elemanlarını dolaşacak olursak:

  • Her eleman için sonuç dizisinde bu elemanın olup olmadığınına bakılacak.
  • Eğer var ise, görmezden gelinecek, diğer türlü sonuç dizisine eklenecek.
function benzersiz(arr) {
  let sonuc = [];

  for (let str of arr) {
    if (!sonuc.includes(str)) {
      sonuc.push(str);
    }
  }

  return sonuc;
}

let kullanicilar = ["Emine", "Muzaffer", "Fatma", "Kanako",
  "Kanako", "Muzaffer", "Fatma", "Kanako", ":-O"
];

alert( benzersiz(kullanicilar) ); // Emine, Muzaffer, Fatma, Kanako, :-O

Kod çalışacaktır, fakat performans problemi yaratabilir.

sonuc.includes(str) sonuc dizisini tamamen dolanır ve str ile karşılaştırır.

Diyelimki sonuc dizisinde 100 tane eleman var ve str ile eşleşen yok. sonuc dizisi dolanılacak ve kesinlikle 100 defa karşılaştırma yapılacaktır. Eğer sonuc dizisi 10000 gibi elemana sahip ise bu karşılaştırma 10000 olacaktır.

Aslında problem bu değildir. JavaScript motoru 10000 karşılaştırmayı mikrosaniyeler içerisinde yapabilir.

Asıl sorun bu testlerin arr’in her bir elemanı için yapılmasıdır.

Yani arr.length 10000 olduğundan dolayı biz 10000*10000 = 100 milyon karşılaştırma olmaktadır.

Bundan dolayı bu çözüm az elemana sahip diziler için kullanılabilir.

Bu optimizasyonu Map, Set, WeakMap ve WeakSet bölümünde göreceksiniz.

function benzersiz(arr) {
  let sonuc = [];

  for (let str of arr) {
    if (!sonuc.includes(str)) {
      sonuc.push(str);
    }
  }

  return sonuc;
}

Çözümü testler korunaklı alanda olacak şekilde aç.

Eğitim haritası