فارسی

قدرت برنامه نویسی تابعی را با آرایه های جاوا اسکریپت باز کنید. با استفاده از متدهای داخلی، داده های خود را به طور موثر تبدیل، فیلتر و کاهش دهید.

تسلط بر برنامه نویسی تابعی با آرایه های جاوا اسکریپت

در چشم انداز همیشه در حال تحول توسعه وب، جاوا اسکریپت همچنان یک سنگ بنا است. در حالی که پارادایم های برنامه نویسی شی گرا و امری مدت هاست که مسلط بوده اند، برنامه نویسی تابعی (FP) کشش قابل توجهی به دست می آورد. FP بر تغییرناپذیری، توابع خالص و کد اعلانی تأکید دارد، که منجر به برنامه های کاربردی قوی تر، قابل نگهداری تر و قابل پیش بینی تر می شود. یکی از قدرتمندترین راه ها برای پذیرش برنامه نویسی تابعی در جاوا اسکریپت، استفاده از متدهای آرایه بومی آن است.

این راهنمای جامع به بررسی این موضوع می پردازد که چگونه می توانید از قدرت اصول برنامه نویسی تابعی با استفاده از آرایه های جاوا اسکریپت استفاده کنید. ما مفاهیم کلیدی را بررسی خواهیم کرد و نحوه استفاده از آنها را با استفاده از متدهایی مانند map، filter و reduce نشان می دهیم و نحوه مدیریت دستکاری داده ها را متحول می کنیم.

برنامه نویسی تابعی چیست؟

قبل از پرداختن به آرایه های جاوا اسکریپت، اجازه دهید به طور خلاصه برنامه نویسی تابعی را تعریف کنیم. در هسته خود، FP یک پارادایم برنامه نویسی است که محاسبات را به عنوان ارزیابی توابع ریاضی در نظر می گیرد و از تغییر وضعیت و داده های قابل تغییر اجتناب می کند. اصول کلیدی عبارتند از:

اتخاذ این اصول می تواند منجر به کدی شود که استدلال، آزمایش و اشکال زدایی آن آسان تر است، به خصوص در برنامه های کاربردی پیچیده. متدهای آرایه جاوا اسکریپت برای پیاده سازی این مفاهیم کاملاً مناسب هستند.

قدرت متدهای آرایه جاوا اسکریپت

آرایه های جاوا اسکریپت مجهز به مجموعه ای غنی از متدهای داخلی هستند که امکان دستکاری پیچیده داده ها را بدون توسل به حلقه های سنتی (مانند for یا while) فراهم می کنند. این متدها اغلب آرایه های جدیدی را برمی گردانند، تغییرناپذیری را ترویج می کنند و توابع callback را می پذیرند، که رویکردی تابعی را امکان پذیر می کند.

بیایید اساسی ترین متدهای آرایه تابعی را بررسی کنیم:

1. Array.prototype.map()

متد map() یک آرایه جدید ایجاد می کند که با نتایج فراخوانی یک تابع ارائه شده بر روی هر عنصر در آرایه فراخوانی شده پر شده است. برای تبدیل هر عنصر یک آرایه به چیز جدید ایده آل است.

نحوه نوشتن:

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

ویژگی های کلیدی:

مثال: دو برابر کردن هر عدد

تصور کنید یک آرایه از اعداد دارید و می خواهید یک آرایه جدید ایجاد کنید که در آن هر عدد دو برابر شود.

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

// Using map for transformation
const doubledNumbers = numbers.map(number => number * 2);

console.log(numbers); // Output: [1, 2, 3, 4, 5] (original array is unchanged)
console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

مثال: استخراج ویژگی ها از اشیاء

یک مورد استفاده رایج، استخراج ویژگی های خاص از یک آرایه از اشیاء است. فرض کنید لیستی از کاربران داریم و می خواهیم فقط نام آنها را بدست آوریم.

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

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

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

2. Array.prototype.filter()

متد filter() یک آرایه جدید با تمام عناصری ایجاد می کند که آزمون پیاده سازی شده توسط تابع ارائه شده را پشت سر می گذارند. برای انتخاب عناصر بر اساس یک شرط استفاده می شود.

نحوه نوشتن:

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

ویژگی های کلیدی:

مثال: فیلتر کردن اعداد زوج

بیایید آرایه اعداد را فیلتر کنیم تا فقط اعداد زوج را نگه داریم.

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

// Using filter to select even numbers
const evenNumbers = numbers.filter(number => number % 2 === 0);

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

مثال: فیلتر کردن کاربران فعال

از آرایه کاربران خود، بیایید کاربرانی را که به عنوان فعال علامت گذاری شده اند، فیلتر کنیم.

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); 
/* Output:
[
  { id: 1, name: 'Alice', isActive: true },
  { id: 3, name: 'Charlie', isActive: true }
]
*/

3. Array.prototype.reduce()

متد reduce() یک تابع callback با عنوان "کاهش دهنده" ارائه شده توسط کاربر را بر روی هر عنصر از آرایه، به ترتیب، اجرا می کند، و مقدار بازگشتی از محاسبه را روی عنصر قبلی عبور می دهد. نتیجه نهایی اجرای کاهش دهنده در تمام عناصر آرایه، یک مقدار واحد است.

این به جرات متنوع ترین متد از متدهای آرایه است و سنگ بنای بسیاری از الگوهای برنامه نویسی تابعی است، به شما این امکان را می دهد که یک آرایه را به یک مقدار واحد "کاهش" دهید (به عنوان مثال، مجموع، ضرب، شمارش یا حتی یک شی یا آرایه جدید).

نحوه نوشتن:

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

ویژگی های کلیدی:

مثال: جمع کردن اعداد

بیایید تمام اعداد موجود در آرایه خود را جمع کنیم.

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

// Using reduce to sum numbers
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); // 0 is the initialValue

console.log(sum); // Output: 15

توضیحات:

مثال: گروه بندی اشیاء بر اساس یک ویژگی

ما می توانیم از reduce برای تبدیل یک آرایه از اشیاء به یک شی استفاده کنیم که در آن مقادیر بر اساس یک ویژگی خاص گروه بندی می شوند. بیایید کاربران خود را بر اساس وضعیت `isActive` آنها گروه بندی کنیم.

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;
}, {}); // Empty object {} is the initialValue

console.log(groupedUsers);
/* Output:
{
  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 }
  ]
}
*/

مثال: شمارش وقوع

بیایید فراوانی هر میوه را در یک لیست بشماریم.

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); // Output: { apple: 3, banana: 2, orange: 1 }

4. Array.prototype.forEach()

در حالی که forEach() یک آرایه جدید بر نمی گرداند و اغلب امری تر در نظر گرفته می شود زیرا هدف اصلی آن اجرای یک تابع برای هر عنصر آرایه است، اما همچنان یک متد اساسی است که در الگوهای تابعی نقش دارد، به ویژه زمانی که اثرات جانبی ضروری هستند یا هنگام تکرار بدون نیاز به خروجی تبدیل شده.

نحوه نوشتن:

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

ویژگی های کلیدی:

مثال: ورود هر عنصر

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

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

نکته: برای تبدیل و فیلتر کردن، map و filter به دلیل تغییرناپذیری و ماهیت اعلانی آنها ترجیح داده می شوند. از forEach زمانی استفاده کنید که به طور خاص نیاز به انجام یک عمل برای هر مورد بدون جمع آوری نتایج در یک ساختار جدید دارید.

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

این متدها برای یافتن عناصر خاص در یک آرایه مفید هستند.

مثال: یافتن یک کاربر

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); // Output: { id: 2, name: 'Bob' }
console.log(bobIndex); // Output: 1
console.log(nonExistentUser); // Output: undefined
console.log(nonExistentIndex); // Output: -1

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

این متدها آزمایش می کنند که آیا تمام عناصر در آرایه از آزمایش پیاده سازی شده توسط تابع ارائه شده عبور می کنند یا خیر.

مثال: بررسی وضعیت کاربر

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); // Output: true (because Bob is inactive)
console.log(allAreActive); // Output: false (because Bob is inactive)

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

// Alternative using every directly
const allUsersActiveDirect = users.every(user => user.isActive);
console.log(allUsersActiveDirect); // Output: false

زنجیره ای کردن متدهای آرایه برای عملیات پیچیده

قدرت واقعی برنامه نویسی تابعی با آرایه های جاوا اسکریپت زمانی می درخشد که این متدها را به هم زنجیره می کنید. از آنجایی که بیشتر این متدها آرایه های جدیدی را برمی گردانند (به جز forEach)، می توانید به طور یکپارچه خروجی یک متد را به ورودی دیگری هدایت کنید و خطوط لوله داده زیبا و قابل خواندن ایجاد کنید.

مثال: یافتن نام کاربران فعال و دو برابر کردن شناسه های آنها

بیایید تمام کاربران فعال را پیدا کنیم، نام آنها را استخراج کنیم و سپس یک آرایه جدید ایجاد کنیم که در آن هر نام با یک عدد که نشان دهنده اندیس آن در لیست *فیلتر شده* است، و شناسه های آنها دو برابر شود، پیشوند شود.

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) // Get only active users
  .map((user, index) => ({
    name: `${index + 1}. ${user.name}`,
    doubledId: user.id * 2
  }));

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

این رویکرد زنجیره ای اعلانی است: ما مراحل (فیلتر، سپس map) را بدون مدیریت حلقه صریح مشخص می کنیم. همچنین تغییرناپذیر است، زیرا هر مرحله یک آرایه یا شی جدید تولید می کند و آرایه اصلی users را دست نخورده باقی می گذارد.

تغییرناپذیری در عمل

برنامه نویسی تابعی به شدت به تغییرناپذیری متکی است. این بدان معناست که به جای تغییر ساختارهای داده موجود، ساختارهای جدیدی را با تغییرات مورد نظر ایجاد می کنید. متدهای آرایه جاوا اسکریپت مانند map، filter و slice ذاتاً از این امر با برگرداندن آرایه های جدید پشتیبانی می کنند.

چرا تغییرناپذیری مهم است؟

هنگامی که نیاز به انجام عملیاتی دارید که به طور سنتی یک آرایه را تغییر می دهد (مانند اضافه یا حذف یک عنصر)، می توانید با استفاده از متدهایی مانند slice، نحوه نوشتن گسترده (...) یا با ترکیب سایر متدهای تابعی، به تغییرناپذیری دست یابید.

مثال: اضافه کردن یک عنصر به طور تغییرناپذیر

const originalArray = [1, 2, 3];

// Imperative way (mutates originalArray)
// originalArray.push(4);

// Functional way using spread syntax
const newArrayWithPush = [...originalArray, 4];
console.log(originalArray); // Output: [1, 2, 3]
console.log(newArrayWithPush); // Output: [1, 2, 3, 4]

// Functional way using slice and concatenation (less common now)
const newArrayWithSlice = originalArray.slice(0, originalArray.length).concat(4);
console.log(newArrayWithSlice); // Output: [1, 2, 3, 4]

مثال: حذف یک عنصر به طور تغییرناپذیر

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

// Remove element at index 2 (value 3)

// Functional way using slice and spread syntax
const newArrayAfterSplice = [
  ...originalArray.slice(0, 2),
  ...originalArray.slice(3)
];
console.log(originalArray); // Output: [1, 2, 3, 4, 5]
console.log(newArrayAfterSplice); // Output: [1, 2, 4, 5]

// Using filter to remove a specific value
const newValueToRemove = 3;
const arrayWithoutValue = originalArray.filter(item => item !== newValueToRemove);
console.log(arrayWithoutValue); // Output: [1, 2, 4, 5]

بهترین شیوه ها و تکنیک های پیشرفته

با آسوده تر شدن با متدهای آرایه تابعی، این شیوه ها را در نظر بگیرید:

مثال: رویکرد تابعی به تجمیع داده ها

تصور کنید داده های فروش از مناطق مختلف دارید و می خواهید مجموع فروش را برای هر منطقه محاسبه کنید، سپس منطقه ای را با بالاترین فروش پیدا کنید.

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. Calculate total sales per region using reduce
const salesByRegion = salesData.reduce((acc, sale) => {
  acc[sale.region] = (acc[sale.region] || 0) + sale.amount;
  return acc;
}, {});

// salesByRegion will be: { North: 310, South: 330, East: 200 }

// 2. Convert the aggregated object into an array of objects for further processing
const salesArray = Object.keys(salesByRegion).map(region => ({
  region: region,
  totalAmount: salesByRegion[region]
}));

// salesArray will be: [
//   { region: 'North', totalAmount: 310 },
//   { region: 'South', totalAmount: 330 },
//   { region: 'East', totalAmount: 200 }
// ]

// 3. Find the region with the highest sales using reduce
const highestSalesRegion = salesArray.reduce((max, current) => {
  return current.totalAmount > max.totalAmount ? current : max;
}, { region: '', totalAmount: -Infinity }); // Initialize with a very small number

console.log('Sales by Region:', salesByRegion);
console.log('Sales Array:', salesArray);
console.log('Region with Highest Sales:', highestSalesRegion);

/*
Output:
Sales by Region: { North: 310, South: 330, East: 200 }
Sales Array: [
  { region: 'North', totalAmount: 310 },
  { region: 'South', totalAmount: 330 },
  { region: 'East', totalAmount: 200 }
]
Region with Highest Sales: { region: 'South', totalAmount: 330 }
*/

نتیجه گیری

برنامه نویسی تابعی با آرایه های جاوا اسکریپت فقط یک انتخاب سبکی نیست. این یک روش قدرتمند برای نوشتن کد تمیزتر، قابل پیش بینی تر و قوی تر است. با پذیرش متدهایی مانند map، filter و reduce، می توانید به طور موثر داده های خود را تبدیل، پرس و جو و جمع آوری کنید در حالی که به اصول اصلی برنامه نویسی تابعی، به ویژه تغییرناپذیری و توابع خالص، پایبند هستید.

همانطور که به سفر خود در توسعه جاوا اسکریپت ادامه می دهید، ادغام این الگوهای تابعی در گردش کار روزانه شما بدون شک منجر به برنامه های کاربردی قابل نگهداری و مقیاس پذیرتر خواهد شد. با آزمایش این متدهای آرایه در پروژه های خود شروع کنید و به زودی ارزش بی اندازه آنها را کشف خواهید کرد.