Türkçe

JavaScript dizileriyle fonksiyonel programlamanın gücünü açığa çıkarın. Yerleşik yöntemleri kullanarak verilerinizi verimli bir şekilde dönüştürmeyi, filtrelemeyi ve azaltmayı öğrenin.

JavaScript Dizileriyle Fonksiyonel Programlamada Uzmanlaşmak

Web geliştirmenin sürekli değişen ortamında, JavaScript köşe taşı olmaya devam ediyor. Nesne yönelimli ve zorunlu programlama paradigmaları uzun süredir baskın olsa da, fonksiyonel programlama (FP) önemli ölçüde ilgi kazanıyor. FP, değişmezliği, saf fonksiyonları ve bildirimsel kodu vurgulayarak daha sağlam, bakımı kolay ve öngörülebilir uygulamalara yol açar. JavaScript'te fonksiyonel programlamayı benimsemenin en güçlü yollarından biri, yerel dizi yöntemlerinden yararlanmaktır.

Bu kapsamlı kılavuz, JavaScript dizilerini kullanarak fonksiyonel programlama ilkelerinin gücünden nasıl yararlanabileceğinizi ayrıntılı olarak inceleyecektir. Temel kavramları keşfedecek ve veri manipülasyonunu nasıl ele aldığınızı dönüştürerek map, filter ve reduce gibi yöntemleri kullanarak bunları nasıl uygulayacağınızı göstereceğiz.

Fonksiyonel Programlama Nedir?

JavaScript dizilerine dalmadan önce, fonksiyonel programlamayı kısaca tanımlayalım. Özünde FP, hesaplamayı matematiksel fonksiyonların değerlendirilmesi olarak ele alan ve durumu ve değiştirilebilir verileri değiştirmekten kaçınan bir programlama paradigmasıdır. Temel ilkeler şunları içerir:

Bu ilkelerin benimsenmesi, özellikle karmaşık uygulamalarda, hakkında akıl yürütmesi, test etmesi ve hata ayıklaması daha kolay olan bir koda yol açabilir. JavaScript'in dizi yöntemleri, bu kavramları uygulamak için mükemmeldir.

JavaScript Dizi Yöntemlerinin Gücü

JavaScript dizileri, geleneksel döngülere (for veya while gibi) başvurmadan karmaşık veri manipülasyonuna izin veren zengin bir yerleşik yöntemler kümesiyle birlikte gelir. Bu yöntemler genellikle değişmezliği destekleyen yeni diziler döndürür ve fonksiyonel bir yaklaşımı mümkün kılan geri arama fonksiyonlarını kabul eder.

En temel fonksiyonel dizi yöntemlerini keşfedelim:

1. Array.prototype.map()

map() yöntemi, çağıran dizideki her öğe üzerinde sağlanan bir fonksiyonu çağırmanın sonuçlarıyla doldurulmuş yeni bir dizi oluşturur. Bir dizinin her öğesini yeni bir şeye dönüştürmek için idealdir.

Sözdizimi:

array.map(callback(currentValue[, index[, array]])[, thisArg])

Temel Özellikler:

Örnek: Her Sayıyı İkiye Katlama

Bir sayı diziniz olduğunu ve her sayının ikiye katlandığı yeni bir dizi oluşturmak istediğinizi hayal edin.

const numbers = [1, 2, 3, 4, 5];

// Dönüşüm için map kullanma
const doubledNumbers = numbers.map(number => number * 2);

console.log(numbers); // Çıktı: [1, 2, 3, 4, 5] (orijinal dizi değişmedi)
console.log(doubledNumbers); // Çıktı: [2, 4, 6, 8, 10]

Örnek: Nesnelerden Özellikleri Çıkarma

Yaygın bir kullanım durumu, bir nesne dizisinden belirli özellikleri çıkarmaktır. Bir kullanıcı listemiz olduğunu ve sadece adlarını almak istediğimizi varsayalım.

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
];

const userNames = users.map(user => user.name);

console.log(userNames); // Çıktı: ['Alice', 'Bob', 'Charlie']

2. Array.prototype.filter()

filter() yöntemi, sağlanan fonksiyon tarafından uygulanan testi geçen tüm öğelerle yeni bir dizi oluşturur. Bir koşula göre öğeleri seçmek için kullanılır.

Sözdizimi:

array.filter(callback(element[, index[, array]])[, thisArg])

Temel Özellikler:

Örnek: Çift Sayıları Filtreleme

Sadece çift sayıları tutmak için sayı dizisini filtreleyelim.

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Çift sayıları seçmek için filter kullanma
const evenNumbers = numbers.filter(number => number % 2 === 0);

console.log(numbers); // Çıktı: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(evenNumbers); // Çıktı: [2, 4, 6, 8, 10]

Örnek: Aktif Kullanıcıları Filtreleme

Kullanıcı dizimizden, aktif olarak işaretlenmiş kullanıcıları filtreleyelim.

const users = [
  { id: 1, name: 'Alice', isActive: true },
  { id: 2, name: 'Bob', isActive: false },
  { id: 3, name: 'Charlie', isActive: true },
  { id: 4, name: 'David', isActive: false }
];

const activeUsers = users.filter(user => user.isActive);

console.log(activeUsers); 
/* Çıktı:
[
  { id: 1, name: 'Alice', isActive: true },
  { id: 3, name: 'Charlie', isActive: true }
]
*/

3. Array.prototype.reduce()

reduce() yöntemi, dizinin her öğesinde, sırayla, kullanıcı tarafından sağlanan bir "reducer" geri arama fonksiyonunu yürütür ve önceki öğedeki hesaplamadan dönüş değerini geçirir. Dizinin tüm öğelerinde reducer'ı çalıştırmanın nihai sonucu tek bir değerdir.

Bu, tartışmasız olarak dizi yöntemlerinin en çok yönlü olanıdır ve birçok fonksiyonel programlama modelinin köşe taşıdır ve bir diziyi tek bir değere (örneğin, toplam, ürün, sayı ve hatta yeni bir nesne veya dizi) "azaltmanıza" olanak tanır.

Sözdizimi:

array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

Temel Özellikler:

Örnek: Sayıları Toplama

Dizimizdeki tüm sayıları toplayalım.

const numbers = [1, 2, 3, 4, 5];

// Sayıları toplamak için reduce kullanma
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // 0 initialValue'dur

console.log(sum); // Çıktı: 15

Açıklama:

Örnek: Nesneleri Bir Özelliğe Göre Gruplandırma

reduce'u, nesne dizisini, değerlerin belirli bir özelliğe göre gruplandırıldığı bir nesneye dönüştürmek için kullanabiliriz. Kullanıcılarımızı `isActive` durumlarına göre gruplandıralım.

const users = [
  { id: 1, name: 'Alice', isActive: true },
  { id: 2, name: 'Bob', isActive: false },
  { id: 3, name: 'Charlie', isActive: true },
  { id: 4, name: 'David', isActive: false }
];

const groupedUsers = users.reduce((acc, user) => {
  const status = user.isActive ? 'active' : 'inactive';
  if (!acc[status]) {
    acc[status] = [];
  }
  acc[status].push(user);
  return acc;
}, {}); // Boş nesne {} initialValue'dur

console.log(groupedUsers);
/* Çıktı:
{
  active: [
    { id: 1, name: 'Alice', isActive: true },
    { id: 3, name: 'Charlie', isActive: true }
  ],
  inactive: [
    { id: 2, name: 'Bob', isActive: false },
    { id: 4, name: 'David', isActive: false }
  ]
}
*/

Örnek: Oluşumları Sayma

Bir listedeki her meyvenin sıklığını sayalım.

const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];

const fruitCounts = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(fruitCounts); // Çıktı: { apple: 3, banana: 2, orange: 1 }

4. Array.prototype.forEach()

forEach() yeni bir dizi döndürmese ve birincil amacı her dizi öğesi için bir fonksiyonu yürütmek olduğundan genellikle daha zorunlu olarak kabul edilse de, yine de fonksiyonel modellerde rol oynayan temel bir yöntemdir, özellikle yan etkiler gerektiğinde veya dönüştürülmüş bir çıktıya ihtiyaç duymadan yineleme yapıldığında.

Sözdizimi:

array.forEach(callback(element[, index[, array]])[, thisArg])

Temel Özellikler:

Örnek: Her Öğeyi Kaydetme

const messages = ['Hello', 'Functional', 'World'];

messages.forEach(message => console.log(message));
// Çıktı:
// Hello
// Functional
// World

Not: Dönüşümler ve filtreleme için, değişmezlikleri ve bildirimsel doğaları nedeniyle map ve filter tercih edilir. Sonuçları yeni bir yapıda toplamadan her öğe için özellikle bir eylem gerçekleştirmeniz gerektiğinde forEach kullanın.

5. Array.prototype.find() ve Array.prototype.findIndex()

Bu yöntemler, bir dizideki belirli öğeleri bulmak için kullanışlıdır.

Örnek: Bir Kullanıcıyı Bulma

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
];

const bob = users.find(user => user.name === 'Bob');
const bobIndex = users.findIndex(user => user.name === 'Bob');
const nonExistentUser = users.find(user => user.name === 'David');
const nonExistentIndex = users.findIndex(user => user.name === 'David');

console.log(bob); // Çıktı: { id: 2, name: 'Bob' }
console.log(bobIndex); // Çıktı: 1
console.log(nonExistentUser); // Çıktı: undefined
console.log(nonExistentIndex); // Çıktı: -1

6. Array.prototype.some() ve Array.prototype.every()

Bu yöntemler, dizideki tüm öğelerin sağlanan fonksiyon tarafından uygulanan testi geçip geçmediğini test eder.

Örnek: Kullanıcı Durumunu Kontrol Etme

const users = [
  { id: 1, name: 'Alice', isActive: true },
  { id: 2, name: 'Bob', isActive: false },
  { id: 3, name: 'Charlie', isActive: true }
];

const hasInactiveUser = users.some(user => !user.isActive);
const allAreActive = users.every(user => user.isActive);

console.log(hasInactiveUser); // Çıktı: true (çünkü Bob aktif değil)
console.log(allAreActive); // Çıktı: false (çünkü Bob aktif değil)

const allUsersActive = users.filter(user => user.isActive).length === users.length;
console.log(allUsersActive); // Çıktı: false

// Doğrudan every kullanarak alternatif
const allUsersActiveDirect = users.every(user => user.isActive);
console.log(allUsersActiveDirect); // Çıktı: false

Karmaşık İşlemler İçin Dizi Yöntemlerini Zincirleme

JavaScript dizileriyle fonksiyonel programlamanın gerçek gücü, bu yöntemleri birbirine zincirlediğinizde ortaya çıkar. Bu yöntemlerin çoğu yeni diziler (forEach hariç) döndürdüğünden, bir yöntemin çıktısını diğerinin girdisine sorunsuz bir şekilde aktarabilir, zarif ve okunabilir veri işlem hatları oluşturabilirsiniz.

Örnek: Aktif Kullanıcı Adlarını Bulma ve Kimliklerini İkiye Katlama

Tüm aktif kullanıcıları bulalım, adlarını çıkaralım ve ardından her adın *filtrelenmiş* listedeki indeksini temsil eden bir sayıyla eklendiği ve kimliklerinin ikiye katlandığı yeni bir dizi oluşturalım.

const users = [
  { id: 1, name: 'Alice', isActive: true },
  { id: 2, name: 'Bob', isActive: false },
  { id: 3, name: 'Charlie', isActive: true },
  { id: 4, name: 'David', isActive: true },
  { id: 5, name: 'Eve', isActive: false }
];

const processedActiveUsers = users
  .filter(user => user.isActive) // Sadece aktif kullanıcıları al
  .map((user, index) => ({
    name: `${index + 1}. ${user.name}`,
    doubledId: user.id * 2
  }));

console.log(processedActiveUsers);
/* Çıktı:
[
  { name: '1. Alice', doubledId: 2 },
  { name: '2. Charlie', doubledId: 6 },
  { name: '3. David', doubledId: 8 }
]
*/

Bu zincirleme yaklaşımı bildirimseldir: açık döngü yönetimi olmadan adımları (filtrele, sonra haritala) belirtiriz. Ayrıca, her adım orijinal users dizisini dokunulmadan bırakarak yeni bir dizi veya nesne ürettiğinden değişmezdir.

Pratikte Değişmezlik

Fonksiyonel programlama büyük ölçüde değişmezliğe dayanır. Bu, mevcut veri yapılarını değiştirmek yerine, istenen değişikliklerle yenilerini oluşturduğunuz anlamına gelir. JavaScript'in map, filter ve slice gibi dizi yöntemleri, yeni diziler döndürerek bunu doğal olarak destekler.

Değişmezlik neden önemlidir?

Geleneksel olarak bir diziyi değiştirecek (bir öğe eklemek veya kaldırmak gibi) bir işlem gerçekleştirmeniz gerektiğinde, slice, yayılma sözdizimi (...) gibi yöntemleri kullanarak veya diğer fonksiyonel yöntemleri birleştirerek değişmezliği sağlayabilirsiniz.

Örnek: Bir Öğeyi Değişmez Şekilde Ekleme

const originalArray = [1, 2, 3];

// Zorunlu yol (originalArray'i değiştirir)
// originalArray.push(4);

// Yayılma sözdizimini kullanarak fonksiyonel yol
const newArrayWithPush = [...originalArray, 4];
console.log(originalArray); // Çıktı: [1, 2, 3]
console.log(newArrayWithPush); // Çıktı: [1, 2, 3, 4]

// Slice ve birleştirmeyi kullanarak fonksiyonel yol (artık daha az yaygın)
const newArrayWithSlice = originalArray.slice(0, originalArray.length).concat(4);
console.log(newArrayWithSlice); // Çıktı: [1, 2, 3, 4]

Örnek: Bir Öğeyi Değişmez Şekilde Kaldırma

const originalArray = [1, 2, 3, 4, 5];

// 2 indeksindeki öğeyi kaldır (değer 3)

// Slice ve yayılma sözdizimini kullanarak fonksiyonel yol
const newArrayAfterSplice = [
  ...originalArray.slice(0, 2),
  ...originalArray.slice(3)
];
console.log(originalArray); // Çıktı: [1, 2, 3, 4, 5]
console.log(newArrayAfterSplice); // Çıktı: [1, 2, 4, 5]

// Belirli bir değeri kaldırmak için filter kullanma
const newValueToRemove = 3;
const arrayWithoutValue = originalArray.filter(item => item !== newValueToRemove);
console.log(arrayWithoutValue); // Çıktı: [1, 2, 4, 5]

En İyi Uygulamalar ve Gelişmiş Teknikler

Fonksiyonel dizi yöntemlerinde daha rahat hale geldikçe, bu uygulamaları göz önünde bulundurun:

Örnek: Veri Toplamaya Fonksiyonel Yaklaşım

Farklı bölgelerden satış verileriniz olduğunu ve her bölge için toplam satışları hesaplamak, ardından en yüksek satışlara sahip bölgeyi bulmak istediğinizi hayal edin.

const salesData = [
  { region: 'North', amount: 100 },
  { region: 'South', amount: 150 },
  { region: 'North', amount: 120 },
  { region: 'East', amount: 200 },
  { region: 'South', amount: 180 },
  { region: 'North', amount: 90 }
];

// 1. Reduce kullanarak bölge başına toplam satışları hesaplayın
const salesByRegion = salesData.reduce((acc, sale) => {
  acc[sale.region] = (acc[sale.region] || 0) + sale.amount;
  return acc;
}, {});

// salesByRegion şöyle olacaktır: { North: 310, South: 330, East: 200 }

// 2. Toplanan nesneyi daha fazla işlenmesi için bir nesne dizisine dönüştürün
const salesArray = Object.keys(salesByRegion).map(region => ({
  region: region,
  totalAmount: salesByRegion[region]
}));

// salesArray şöyle olacaktır: [
//   { region: 'North', totalAmount: 310 },
//   { region: 'South', totalAmount: 330 },
//   { region: 'East', totalAmount: 200 }
// ]

// 3. Reduce kullanarak en yüksek satışlara sahip bölgeyi bulun
const highestSalesRegion = salesArray.reduce((max, current) => {
  return current.totalAmount > max.totalAmount ? current : max;
}, { region: '', totalAmount: -Infinity }); // Çok küçük bir sayıyla başlatın

console.log('Bölgelere Göre Satışlar:', salesByRegion);
console.log('Satış Dizisi:', salesArray);
console.log('En Yüksek Satışlara Sahip Bölge:', highestSalesRegion);

/*
Çıktı:
Bölgelere Göre Satışlar: { North: 310, South: 330, East: 200 }
Satış Dizisi: [
  { region: 'North', totalAmount: 310 },
  { region: 'South', totalAmount: 330 },
  { region: 'East', totalAmount: 200 }
]
En Yüksek Satışlara Sahip Bölge: { region: 'South', totalAmount: 330 }
*/

Sonuç

JavaScript dizileriyle fonksiyonel programlama sadece stilistik bir seçim değildir; daha temiz, daha öngörülebilir ve daha sağlam kod yazmanın güçlü bir yoludur. map, filter ve reduce gibi yöntemleri benimseyerek, fonksiyonel programlamanın temel ilkelerine, özellikle değişmezliğe ve saf fonksiyonlara bağlı kalarak verilerinizi etkili bir şekilde dönüştürebilir, sorgulayabilir ve toplayabilirsiniz.

JavaScript geliştirmedeki yolculuğunuza devam ederken, bu fonksiyonel desenleri günlük iş akışınıza entegre etmek şüphesiz daha sürdürülebilir ve ölçeklenebilir uygulamalara yol açacaktır. Projelerinizde bu dizi yöntemlerini deneyerek başlayın ve kısa süre sonra onların muazzam değerini keşfedeceksiniz.