`Array` nesnesi

`Array` nesnesi

Bu makalede Array nesnesi açıklanmaktadır.

Dizilerin pratik kullanımlarını kolay anlaşılır bir şekilde adım adım açıklayacağım.

YouTube Video

Array nesnesi

JavaScript'teki Array nesnesi, her türlü veri işlemenin temelini oluşturan en önemli yapılardan biridir. Temel dizi işlemlerinden, verimli veri dönüşümü için faydalı olan üst düzey fonksiyonlara kadar bilmeniz gereken birçok özellik vardır.

Dizi Temelleri

JavaScript'te diziler, birden fazla değeri birlikte işlemek için temel bir veri yapısıdır. Burada, dizilerin nasıl oluşturulacağını ve elemanlarının nasıl okunup yazılacağını basit örneklerle açıklıyoruz.

 1// Create arrays in different ways
 2const arr1 = [1, 2, 3];           // array literal
 3const arr2 = new Array(4, 5, 6);  // Array constructor
 4const arr3 = Array.of(7, 8, 9);   // Array.of
 5
 6console.log("arr1 created with literal.   :", arr1);
 7console.log("arr2 created with constructor:", arr2);
 8console.log("arr3 created with Array.of.  :", arr3);
 9// arr1 created with literal.   : [ 1, 2, 3 ]
10// arr2 created with constructor: [ 4, 5, 6 ]
11// arr3 created with Array.of.  : [ 7, 8, 9 ]
12
13// Access and modify elements
14let first = arr1[0];              // read element
15console.log("First element of arr1:", first);
16// First element of arr1: 1
17
18arr1[1] = 20;                     // modify element
19console.log("arr1 after modifying index 1:", arr1);
20// arr1 after modifying index 1: [ 1, 20, 3 ]
21
22const len = arr1.length;          // get length
23console.log("Length of arr1:", len);
24// Length of arr1: 3
  • Bu kod, dizileri oluşturmanın üç yolunu, indeks kullanarak öğelerin nasıl okunup güncelleneceğini ve length özelliği ile uzunluğun nasıl alınacağını gösteriyor.
  • Dizi literal'leri en yaygın ve okunaklı olanlardır ve günlük durumlarda en sık kullanılırlar.

Eleman Ekleme ve Çıkarma (Sonda veya Başta)

Diziler, baştan veya sondan kolayca eleman ekleyip çıkarmanıza olanak tanır. Bu işlemler, yığın (stack) veya kuyruk (queue) gibi yapılar oluştururken de faydalıdır.

 1// Push and pop (stack-like)
 2const stack = [];
 3console.log("Initial stack:", stack);
 4
 5stack.push(1);                    // push 1
 6console.log("After push(1):", stack);
 7
 8stack.push(2);                    // push 2
 9console.log("After push(2):", stack);
10
11const last = stack.pop();         // pop -> 2
12console.log("Popped value:", last);
13console.log("Stack after pop():", stack);
14
15// Unshift and shift (queue-like)
16const queue = [];
17console.log("Initial queue:", queue);
18
19queue.push('a');                  // add to end
20console.log("After push('a'):", queue);
21
22queue.unshift('start');           // add to front
23console.log("After unshift('start'):", queue);
24
25const firstItem = queue.shift();  // remove from front
26console.log("Shifted value:", firstItem);
27console.log("Queue after shift():", queue);
28
29// Initial stack: []
30// After push(1): [ 1 ]
31// After push(2): [ 1, 2 ]
32// Popped value: 2
33// Stack after pop(): [ 1 ]
34
35// Initial queue: []
36// After push('a'): [ 'a' ]
37// After unshift('start'): [ 'start', 'a' ]
38// Shifted value: start
39// Queue after shift(): [ 'a' ]
  • push ve pop, bir dizinin sonunda çalışır. Yığın (stack) yapıları oluşturmak için idealdirler.
  • unshift ve shift, bir dizinin başında çalışır. Ancak, başta yapılan işlemler tüm eleman indekslerinin dahili olarak kaydırılmasını gerektirir ve bu da maliyetli bir işlemdir.

Ortadaki Elemanların Yönetilmesi (splice ve slice)

Bir dizinin ortasındaki elemanlarla çalışırken, orijinal diziyi değiştirip değiştirmemek istediğinize göre splice veya slice seçin. Bir dizinin sadece bir kısmını çıkarmak istiyorsanız slice kullanın; dizinin kendisini değiştirmek, örneğin eleman eklemek veya silmek istiyorsanız, splice kullanın.

 1// slice (non-destructive)
 2const nums = [0, 1, 2, 3, 4];
 3console.log("Original nums:", nums);
 4
 5const part = nums.slice(1, 4); // returns [1, 2, 3]
 6console.log("Result of nums.slice(1, 4):", part);
 7console.log("nums after slice (unchanged):", nums);
 8
 9// splice (destructive)
10const arr = [10, 20, 30, 40];
11console.log("\nOriginal arr:", arr);
12
13// remove 1 item at index 2, insert 25 and 27
14arr.splice(2, 1, 25, 27);
15console.log("After arr.splice(2, 1, 25, 27):", arr);
16
17// Original nums: [ 0, 1, 2, 3, 4 ]
18// Result of nums.slice(1, 4): [ 1, 2, 3 ]
19// nums after slice (unchanged): [ 0, 1, 2, 3, 4 ]
20
21// Original arr: [ 10, 20, 30, 40 ]
22// After arr.splice(2, 1, 25, 27): [ 10, 20, 25, 27, 40 ]
  • slice, yalnızca elemanları çıkarır ve orijinal diziyi değiştirmez.
  • splice, eleman ekler veya çıkarır ve dizinin kendisini değiştirir, bu yüzden davranışa etkisi konusunda dikkatli olun.

Döngü (for / for...of / forEach)

Dizileri sırayla işlemek için birkaç farklı yol vardır ve amacınıza veya kodlama tercihinize göre seçebilirsiniz. İşte üç tipik döngü yapısı.

 1const items = ['apple', 'banana', 'cherry'];
 2console.log("Items:", items);
 3
 4// classic for
 5console.log("\n--- Classic for loop ---");
 6for (let i = 0; i < items.length; i++) {
 7  console.log(`Index: ${i}, Value: ${items[i]}`);
 8}
 9
10// for...of (recommended for values)
11console.log("\n--- for...of loop ---");
12for (const item of items) {
13  console.log(`Value: ${item}`);
14}
15
16// forEach (functional style)
17console.log("\n--- forEach loop ---");
18items.forEach((item, index) => {
19  console.log(`Index: ${index}, Value: ${item}`);
20});
  • for döngüsü en esnek olanıdır; indeks işlemleri ve break gibi ifadelerle döngüyü hassas şekilde kontrol etmenizi sağlar.
  • for...of, eleman değerleriyle çalışmanın kısa bir yolunu sunar ve okunabilirlik açısından en dengelilerinden biridir.
  • forEach, fonksiyonel tarzda kod yazımına imkan tanır ve her eleman için loglama veya veri güncelleme gibi yan etki işlemleri için uygundur. Ancak, break veya continue kullanamazsınız ve await ile asenkron işlemler için uygun değildir.

map / filter / reduce — Üst Düzey Fonksiyonlar

map, filter ve reduce, dizileri dönüştürürken, filtrelerken veya birleştirirken sıkça kullanılan yüksek dereceli fonksiyonlardır. Tekrarlayan işlemleri açıkça ifade edebildiğiniz için kodunuz basit ve anlaşılır olur.

 1const numbers = [1, 2, 3, 4, 5];
 2console.log("Original numbers:", numbers);
 3
 4// map: transform each item
 5const doubled = numbers.map(n => n * 2);
 6console.log("\nResult of map (n * 2):", doubled);
 7
 8// filter: select items
 9const evens = numbers.filter(n => n % 2 === 0);
10console.log("Result of filter (even numbers):", evens);
11
12// reduce: accumulate to single value
13const sum = numbers.reduce((acc, n) => acc + n, 0);
14console.log("Result of reduce (sum):", sum);
15
16// Original numbers: [ 1, 2, 3, 4, 5 ]
17// Result of map (n * 2): [ 2, 4, 6, 8, 10 ]
18// Result of filter (even numbers): [ 2, 4 ]
19// Result of reduce (sum): 15
  • Bu yöntemler, yapmak istediğiniz işlemi deklaratif bir şekilde ifade etmenizi sağlar, böylece okunabilirliği artırır ve istenmeyen yan etkilerden kaçınmanıza yardımcı olur.

find / findIndex / some / every

İşte arama ve koşul kontrol yöntemlerinin genel bir özeti. Bunlar, belirli koşulları karşılayan elemanları bulmak veya küme üzerinde boolean (doğru/yanlış) kontroller yapmak için faydalıdır.

 1const users = [
 2  { id: 1, name: 'Alice' },
 3  { id: 2, name: 'Bob' },
 4  { id: 3, name: 'Carol' }
 5];
 6
 7console.log("Users:", users);
 8
 9// Find the first user whose name is 'Bob'
10const bob = users.find(user => user.name === 'Bob');
11console.log("\nResult of find (name === 'Bob'):", bob);
12
13// Find index of the user whose id is 3
14const indexOfId3 = users.findIndex(user => user.id === 3);
15console.log("Result of findIndex (id === 3):", indexOfId3);
16
17// Check if there exists a user with id = 2
18const hasId2 = users.some(user => user.id === 2);
19console.log("Result of some (id === 2):", hasId2);
20
21// Check if all users have a numeric id
22const allHaveNumericId = users.every(user => typeof user.id === 'number');
23console.log("Result of every (id is number):", allHaveNumericId);
24// Result of find (name === 'Bob'): { id: 2, name: 'Bob' }
25// Result of findIndex (id === 3): 2
26// Result of some (id === 2): true
27// Result of every (id is number): true
  • find, koşulu sağlayan ilk elemanı döndürür.
  • findIndex, koşulu sağlayan elemanın indeksini döndürür.
  • some, eğer koşulu sağlayan en az bir eleman varsa true döndürür.
  • every, tüm elemanlar koşulu sağlıyorsa true döndürür.

Bunların hepsi dizi işlemlerinde çok faydalıdır; duruma uygun kullandığınızda kodunuz kısa ve anlaşılır olur.

Sıralama ve Karşılaştırma Fonksiyonları

Diziler sort ile sıralanır, ancak varsayılan olarak elemanları string (metin) olarak karşılaştırır, bu nedenle sayıları sıralarken beklenmedik sonuçlar ortaya çıkabilir.

 1const nums = [10, 2, 33, 4];
 2console.log("Original nums:", nums);
 3
 4// Default sort: compares elements as strings (not suitable for numbers)
 5nums.sort();
 6console.log("\nAfter default sort (string comparison):", nums);
 7
 8// Numeric ascending sort using a compare function
 9nums.sort((a, b) => a - b);
10console.log("After numeric sort (a - b):", nums);
11
12// Sort objects by a property
13const people = [{ age: 30 }, { age: 20 }, { age: 25 }];
14console.log("\nOriginal people:", people);
15
16people.sort((a, b) => a.age - b.age);
17console.log("After sorting people by age:", people);
18
19// After default sort (string comparison): [ 10, 2, 33, 4 ]
20// After numeric sort (a - b): [ 2, 4, 10, 33 ]
21// Original people: [ { age: 30 }, { age: 20 }, { age: 25 } ]
22// After sorting people by age: [ { age: 20 }, { age: 25 }, { age: 30 } ]
  • Sayıları veya nesneleri sıralarken, onları istediğiniz sıraya göre sıralamak için mutlaka bir karşılaştırma fonksiyonu belirtin.
  • Bir karşılaştırma fonksiyonunda, negatif dönüş ab'den önce, pozitif ise byi adan önce, 0 ise sıralamayı değiştirmez.

Dizi Kopyalama ve Değişmezlik

Dizileri kopyalarken, 'referans kopyalama' ile 'yüzeysel kopyalama' arasındaki farkı anlamak önemlidir. Özellikle, dizinin içinde nesneler varsa, yüzeysel bir kopyanın iç nesnelerin paylaşılmasına sebep olacağını unutmayın.

Referans kopyası

Bir diziyi başka bir değişkene atadığınızda, dizinin kendisi çoğaltılmaz; bunun yerine aynı diziye işaret eden 'referans' kopyalanır.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Reference copy; modifying b also affects a
 5const b = a;
 6console.log("\nReference copy b = a:", b);
 7// Reference copy b = a: [ 1, 2, 3 ]
 8
 9b[0] = 100;
10console.log("After modifying b[0] = 100:");
11console.log("a:", a);  // a: [ 100, 2, 3 ] (affected)
12console.log("b:", b);  // b: [ 100, 2, 3 ]
  • Referans kopyası ile, kopyalanan değişkenle dizinin içeriğini değiştirirseniz, bu değişiklikler aynı diziye işaret eden orijinal değişkene de yansır.

Yüzeysel kopya

slice() veya yayma (spread) sözdizimi kullanmak 'yüzeysel kopya' oluşturur çünkü yalnızca değerler çoğaltılır; orijinal ve kopyalanan diziler ayrı varlıklar olarak ele alınır.

 1const a = [1, 2, 3];
 2console.log("Original a:", a);
 3
 4// Shallow copy (new array)
 5const b = a.slice();
 6console.log("\nShallow copy using slice():", b);
 7// Shallow copy using slice(): [ 100, 2, 3 ]
 8
 9const c = [...a];
10console.log("Shallow copy using spread [...a]:", c);
11// Shallow copy using spread [...a]: [ 100, 2, 3 ]
12
13// Modifying c or d does NOT affect a
14b[1] = 200;
15c[2] = 300;
16console.log("\nAfter modifying b[1] = 200 and c[2] = 300:");
17console.log("a:", a);   // [ 100, 2, 3 ]
18console.log("b:", b);   // [ 100, 200, 3 ]
19console.log("c:", c);   // [ 100, 2, 300 ]
  • Bu kod, slice() veya yayma sözdizimi ile oluşturulan bir dizinin yüzeysel kopyasının orijinal diziyi etkilemediğini gösterir.

Yüzeysel kopya ve değişmezlik

'Yüzeysel kopya' ile bir diziyi çoğaltsanız bile, dizinin içinde nesneler varsa istemeden paylaşım gerçekleşebilir.

 1// Shallow copy doesn't clone inner objects
 2const nested = [{ x: 1 }, { x: 2 }];
 3console.log("\nOriginal nested:", nested);
 4// Original nested: [ { x: 1 }, { x: 2 } ]
 5
 6const shallow = nested.slice();
 7console.log("Shallow copy of nested:", shallow);
 8// Shallow copy of nested: [ { x: 1 }, { x: 2 } ]
 9
10// Changing inner object affects both arrays
11shallow[0].x = 99;
12console.log("\nAfter shallow[0].x = 99:");
13console.log("nested:", nested);
14console.log("shallow:", shallow);
15// nested: [ { x: 99 }, { x: 2 } ]
16// shallow: [ { x: 99 }, { x: 2 } ]
  • Bu kod, yüzeysel kopyada iç nesnelerin paylaşıldığını ve bu iç nesnelerde yapılan değişikliklerin hem orijinal diziye hem de kopyaya etki ettiğini gösterir.
  • Bağımsız verilere ihtiyacınız varsa, structuredClone() veya JSON dönüşümü gibi bir 'derin kopya' yapmanız gerekir.

Faydalı yardımcı metotlar

Aşağıdakiler, dizilerle çalışırken sıkça kullanılan yardımcı metotlardır. Amaçlarınıza uygun şekilde kullanarak kısa ve okunabilir kodlar yazabilirsiniz.

includes

includes metodu, belirli bir değerin bir dizi içinde olup olmadığını kontrol eder.

1// includes: check if array contains a value
2const letters = ['a', 'b', 'c'];
3const hasB = letters.includes('b');
4console.log("letters:", letters);  // [ 'a', 'b', 'c' ]
5console.log("letters.includes('b'):", hasB); // true
  • Bu kodda, includes metodu belirli bir değerin dizi içinde olup olmadığını kısa şekilde belirlemek için kullanılmıştır.

concat

concat metodu, belirli bir dizi veya değerleri sona ekleyerek yeni bir dizi döndürür ve orijinal diziyi değiştirmez.

1// concat: merge arrays without mutation
2const a1 = [1, 2];
3const a2 = [3, 4];
4const combined = a1.concat(a2);
5console.log("a1.concat(a2):", combined); // [ 1, 2, 3, 4 ]
6console.log("a1(unchanged):", a1);       // [ 1, 2 ]
  • Bu kod, concat metodunun orijinal dizi korunarak yeni bir dizi oluşturmamıza olanak tanıyan yıkıcı olmayan bir işlem olduğunu gösterir.

flat

flat metodu kullanılarak, iç içe geçmiş diziler düzleştirilebilir.

1// flat and flatMap: flatten arrays or map + flatten in one step
2const nested = [1, [2, [3]]];
3console.log("nested:", nested); // nested: [ 1, [ 2, [ 3 ] ] ]
4console.log("nested.flat():", nested.flat()); // default depth = 1
5// nested.flat(): [ 1, 2, [ 3 ] ]
  • Bu kod, bir dizinin bir seviye düzleştirilmesinin sonucunu gösterir.
  • flat yöntemiyle derinliği belirtebildiğiniz için, ihtiyaçlarınıza göre esnekçe iç içe dizileri çözebilirsiniz.

flatMap

flatMap metodu, her bir elemana bir dönüşüm uyguladıktan sonra sonuçları otomatik olarak tek boyutlu bir diziye düzleştirir.

1// flat and flatMap: flatten arrays or map + flatten in one step
2const words = ['hello world', 'hi'];
3console.log("words:", words); // [ 'hello world', 'hi' ]
4
5const splitWords = words.flatMap(w => w.split(' '));
6console.log("words.flatMap(w => w.split(' ')):", splitWords);
7// words.flatMap(w => w.split(' ')): [ 'hello', 'world', 'hi' ]
  • Bu kod, bir dizideki her bir dizgenin boşluk karakterine göre bölünüp, elde edilen sonuçların bir araya getirilip tek bir diziye düzleştirildiği bir örnek gösterir.

join

join metodu, bir dizinin öğelerini belirtilen bir ayırıcıyla birleştirerek bir dize oluşturur.

1// join: combine elements into a string with a separator
2const csv = ['a', 'b', 'c'].join(',');
3console.log("['a', 'b', 'c'].join(','):", csv);  // a,b,c
  • Bu kodda, join metodu bir diziyi virgülle ayrılmış bir dizeye dönüştürmek için kullanılmıştır.

Yaygın Hatalar

Dizi işlemleri ilk bakışta basit görünebilir, fakat kolayca istenmeyen davranışlara yol açabilecek bazı noktalar vardır. Günlük dizi işlemleri sırasında gözden kaçabilecek birçok hata vardır, bu yüzden aşağıdaki noktalara dikkat etmek kodunuzun güvenilirliğini büyük ölçüde artıracaktır.

  • Varsayılan olarak, Array'nin sort() metodu metin karşılaştırmasıyla sıralar. Sayıları doğru şekilde sıralamak için her zaman bir karşılaştırma fonksiyonu vermek gerekir.
  • Dizileri (slice veya spread (yayma) sözdizimi ile) kopyalamak yüzeysel bir kopya oluşturur. Dizileriniz nesneler içeriyorsa dikkatli olun; orijinal veriler farkında olmadan değiştirilebilir.
  • splice, diziyi doğrudan değiştiren yıkıcı (destructive) bir yöntemdir; slice ise orijinal diziyi değiştirmeyen yıkıcı olmayan (non-destructive) bir yöntemdir. Onları ihtiyacınıza uygun olarak kullanmak önemlidir.
  • forEach await ile asenkron işlemler yapılan döngüler için uygun değildir. Asenkron işlemleri sırayla güvenli şekilde yürütmek istiyorsanız for...of kullanmanız önerilir.

Pratik Örnek

Aşağıda, kullanıcı verilerinden toplam yaşı almak, yaşı 30 ve üzeri olanları çıkarıp isim listesini oluşturmak için dizi metotlarının birleştirildiği bir örnek verilmiştir.

 1const users = [
 2  { name: 'Alice', age: 28 },
 3  { name: 'Bob', age: 34 },
 4  { name: 'Carol', age: 41 },
 5  { name: 'Dave', age: 19 }
 6];
 7
 8console.log("Users:", users);
 9
10// sum ages
11const totalAge = users.reduce((acc, u) => acc + u.age, 0);
12console.log("\nTotal age of all users:", totalAge);
13// Total age of all users: 122
14
15// filter and map names of users 30 and older
16const namesOver30 = users
17  .filter(u => u.age >= 30)
18  .map(u => u.name);
19
20console.log("Users aged 30 or older (names):", namesOver30);
21// Users aged 30 or older (names): [ 'Bob', 'Carol' ]
  • reduce, filter ve map metotlarını zincirleyerek, veri toplama, koşul ile çıkarma ve dönüştürme işlemlerini kolayca yazabilirsiniz.
  • Bu tür bir 'veri işleme hattı' yüksek okunabilirliğe sahiptir ve az yan etki barındıran bir tarz olarak gerçek uygulamalarda sıkça kullanılır.

Özet

JavaScript dizileriyle, temel işlemler bile gayet geniş bir yelpazede uygulanabilir ve üst düzey fonksiyonlar kullanıldığında kodunuz daha kısa ve etkili olur. Anlaşılması gereken çok nokta var, fakat her birini uygun biçimde kullanmayı öğrendiğinizde veri işleme çok daha kolay hale gelecektir.

Yukarıdaki makaleyi, YouTube kanalımızda Visual Studio Code'u kullanarak takip edebilirsiniz. Lütfen YouTube kanalını da kontrol edin.

YouTube Video