تابع ()Array.map در جاوا اسکریپت — راهنمای کاربردی
تابع ()Array.map در جاوا اسکریپت برای بسیاری از توسعهدهندگان مبتدی که قصد دارند درک عمیقتری از «برنامهنویسی تابعی» (Functional Programming) بیابند، یک مانع محسوب میشود. شما پس از یادگیری موارد مختلف در مورد ورودیها و خروجیها در همه انواع حلقههای متفاوت در جاوا اسکریپت، ممکن است تصور کنید که مفهومی به صورت map میتواند کاملاً بیگانه به نظر برسد. این مقاله به عنوان یک راهنمای عمومی و دروازهای به سوی دنیای قدرتمند برنامهنویسی تابعی برای همه افراد مبتدی در این حوزه محسوب میشود. اگر به بررسی عمیقتر مفاهیم برنامهنویسی تابعی در جاوا اسکریپت علاقهمند هستید، این مقاله شروع مناسبی میتواند باشد.
Map ،Maps و Mapping
Map (که نباید آن را با ساختمان داده Map اشتباه گرفت) یک ابزار کاربردی است که میتوان برای تعریف حلقههای تکرار روی آرایه، اعمال نوعی تغییر روی هر مقدار و بازگشت دادن یک آرایه جدید با مقادیر تغییر یافته مورد استفاده قرار داد.
به بیان کلیتر، یک mapping به سادگی نوعی تبدیل یک مقدار به مقدار دیگر است. اگر قرار باشد مقدار 10 را گرفته و مقدار 5 را به آن اضافه کنیم، میتوانیم از تبدیل مقدار 10 به 15 استفاده کنیم. انجام این کار برای همه مقادیر موجود در آرایه و بازگشت دادن یک لیست جدید را میتوان نوعی نگاشت (mapping) روی لیست تصور کرد. در ادامه نگاهی به مثالی از کد map خواهیم داشت:
const arrayToMapOver = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; function timesFive(value) { return value * 5 } const newArray = arrayToMapOver.map(timesFive); // newArray is now [5, 10, 15, 20, 25, 30, 35, 40, 45, 50] // arrayToMapOver is still [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
در این مثال، کار خود را با آرایهای از اعداد از 1 تا 10 آغاز میکنیم. سپس یک تابع به نام ()timesFive تعریف میکنیم که یک مقدار میگیرد و آن را در 5 ضرب کرده و بازگشت میدهد. سپس از متد ()Array.map استفاده میکنیم که روی همه آرایههای جاوا اسکریپت وجود دارد و از آن برای بهکارگیری ()timesFive روی همه مقادیر موجود در آرایه که به متغیر arrayToMapOver محدود میشود استفاده میکنیم. در نهایت خروجی متد map (که آن نیز یک آرایه است) را به متغیری به نام newArray متصل میکنیم.
نکاتی در مورد تغییرناپذیری (Immutability)
لازم به ذکر است که گرچه ما روی همه مقادیر موجود در آرایه arrayToMapOver یک نگاشت انجام میدهیم؛ اما هیچ یک از مقادیر اصلی تغییر نمییابند. این یکی از ارکان اصلی برنامهنویسی تابعی است که تغییرناپذیری یا Immutability نام دارد. بررسی مفهوم تغییرناپذیری تا حدودی خارج از حیطه این مقاله است؛ اما به طور خلاصه میتوان گفت که منظور از تغییرناپذیری این است که دادهها نمیبایست تغییر یابند. در عوض زمانی که نیاز به اصلاح دادههای موجود باشد، میبایست آن دادهها را به شکلی جدید بازسازی کنیم. در مورد مثال فوق، ما نمیتوانیم مقادیر موجود در آرایه arrayToMapOver را تغییر دهیم، بلکه باید مقادیر بازگشتی از ()timesFive را در newArray خودمان ذخیره کنیم.
نوشتن تابع سفارشی برای Map
صحبت کردن در مورد مفهوم map یک چیز است و معرفی عملی آن یک موضوع دیگر، بنابراین در ادامه تلاش میکنیم یک تابع سفارشی برای map بنویسیم. ابتدا باید ساختار تابع خود را تعریف کنیم:
function ourMap(transformation, inputArray) { // our map function logic will go here };
اگر بخواهیم این ساختار را تحلیل کنیم، تابع map ما قرار نیست به صورت یک متد به آرایهای متصل شود، بنابراین باید دو پارامتر/آرگومان به صورت transformation و inputArray داشته باشد. transformation تابعی خواهد بود که روی هر مقدار در آرگومان دوم ما به نام inputArray اعمال میشود. زمانی که امضای تابع ما تعریف شد، مقداری منطق به map خود اضافه میکنیم:
function ourMap(transformation, inputArray) { // bind an empty array to a variable to hold our transformed // values let outputArray = []; return outputArray };
این وضعیت عالی است. ما متغیر outputArray را به یک آرایه خالی متصل کردهایم و از این رو میتوانیم در نهایت مقادیر خروجی خود را ذخیره کنیم. ما همچنین به طور پیشگیرانه آن آرایه خروجی را بازگشت دادهایم. این آرایه فعلاً خالی است؛ اما به زودی پر خواهد شد. جهت افزایش عملکرد، به جای const؛ از کلیدواژه let برای اتصال outputArray استفاده میکنیم. تنها دلیل این مسئله آن است که outputArray به طور الزامی باید اصلاح شده باشد و از این رو اعلان کردن آن به صورت یک const معنی چندانی ندارد. اکنون باید به سمت قطعه بعدی برویم:
function ourMap(transformation, inputArray) { // bind an empty array to a variable to hold our transformed // values let outputArray = []; // loop over the input array for (let value of inputArray) { // apply our transformation here } return outputArray };
شاید از دیدن یک حلقه for تعجب کنید، چون موضوع این مقاله برنامهنویسی تابعی است.
انتزاع برای برنده شدن
یکی از مقاصد اصلی برنامهنویسی تابعی ارائه انتزاع در بخشهای پر استفاده کارکردهای مختلف، در جهت روشنتر شدن کد برای توسعهدهندهای که روی آن کار میکند است. در سطح تئوری، این انتزاع موجب سادهتر شدن کد میشود و نگهداری آن را در بلندمدت سادهتر میسازد. اما زمانی که به صورت عملی آن را بررسی میکنیم، میبینیم هر کدی که در هر سطحی روی یک رایانه اجرا میشود، شامل برخی عملیات پایه شامل گزارههای انتساب، گزارههای شرطی و حلقهها است. برنامههای تابعی نیز از این قاعده مستثنی نیستند. اینک در بخش نهایی کد زیر را داریم:
function ourMap(transformation, inputArray) { // bind an empty array to a variable to hold our transformed // values let outputArray = []; // loop over the input array for (let value of inputArray) { // apply our transformation here let transformedValue = transformation(value) // put the transformed value in our outputArray outputArray.push(transformedValue) } return outputArray };
در این بخش ما به سادگی هر مقدار را در آرایه inputArray خود از طریق حلقه for که قبلاً اضافه کردیم، انتخاب میکنیم و آرگومان transformation را که یک تابع است روی آن اعمال میکنیم. سپس مقدار تبدیل یافته (transformedValue) را میگیریم و آن را به آرایه outputArray میفرستیم. بدین ترتیب کار پایان یافته است. آخرین خط از ourMap آرایه outputArray را با همه مقادیر تبدیل یافته بازمیگرداند. اگر متد ()Array.map قبلی را با پیادهسازی ourMap جایگزین کنیم، دقیقاً عملکرد مشابهی خواهد داشت:
function ourMap(transformation, inputArray) { // bind an empty array to a variable to hold our transformed // values let outputArray = []; // loop over the input array for (let value of inputArray) { // apply our transformation here let transformedValue = transformation(value) // put the transformed value in our outputArray outputArray.push(transformedValue) } return outputArray }; const arrayToMapOver = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; function timesFive(value) { return value * 5 } const newArray = ourMap(timesFive, arrayToMapOver); // newArray is still [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]!
دقت کنید که این پیادهسازی از map کامل نیست و برخی کارکردهای پیشرفته که از چیزی شبیه به ()Array.map میتوان انتظار داشت به دلیل سادهتر شدن بحث، مطرح نشدهاند. بدین ترتیب به پایان این مقاله میرسیم. همان طور که دیدید پیادهسازی تابع سفارشی ما برای map به ما کمک کرده است که به طور عمیقی با طرز کار mapping آشنا شویم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی و برنامهنویسی وب
- آموزش جاوا اسکریپت (JavaScript)
- مجموعه آموزشهای برنامهنویسی
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
- آموزش تعریف توابع در جاوا اسکریپت (JavaScript)
- 1۰ کتابخانه و فریمورک جاوا اسکریپت که باید آنها را بشناسید
- بهینهسازی کدهای جاوا اسکریپت در سال 2۰1۸
- حلقه for در جاوا اسکریپت — از صفر تا صد + مثال و کد
==