آشنایی با تابع reduce در جاوا اسکریپت

زمان مطالعه: 6 دقیقه

با سلام و درود، جبر زمانه مجبورم کرد برم سمت Front-end ، در نتیجه از این به بعد سعی می کنم اگر شد آموزش در مورد JS نیز به اشتراک بگذارم. تو این مطلب میخوام در مورد یکی از توابع موجود برای آرایه در جچاوا اسکریپت صحبت کنم، تابع reduce ، اگر فرانت اند کار باشید، حتما اسم این تابع به گوشتون خورده یا حداقل یبه باری استفاده کردید ازش.

تابع reduce به دلیل وجود داشتن تعریف های مختلف در فضای وب، میتونه فهمش خیلی سخت باشه. گرچه فهم و یادگیری اون خیلی منافع خواهد داشت، از جمله اینکه در مدیریت state نیز استفاده میشه.

خب فرم خام reduce در جاوا اسکریپت (JavaScript) به شکل زیر می باشد (اگر فرم های پیچیده ترش رو دیدید، عجله نداشته باشید، به اونها نیز جلوتر می پردازیم):

arr.reduce(callback, initialValue);

ابتدا کلیات را با چند تا مثال پیش می بریم، سپس مجدد سینتکس کامل reduce را با هم مرور می کنیم.

واژه شناسی

reduce با واژه هایی همچون reducer و accumulator میاد. accumulator مقداریست که با آن تمام می کنیم و reducer عملی است که آن را انجام می دهیم تا به مقداری برسیم.

باید توجه داشته باشید که یک reducer فقط یک مقدار را برخواهد گرداندو فقط یک مقدار نام reduce را خواهد گرفت.

به مثال کلاسیک زیر توجه داشته باشید:

const value = 0;

const numbers = [5, 10, 15];

for(let i = 0; i < numbers.length; i++) {
  value += numbers[i];
}

مثال بالا به ما ۳۰ را خواهد داد (5 + 10 + 15). روش فوق به خوبی کار می کنه، منتهی ما می تونیم همین کار بالا را با reduce انجام دهیم که منجر می شود ما ارزش و مقدار متغیر خود را حفظ کنیم و تغییر ندهیم.

مثال ذیل نیز جوابش میشه ۳۰، منتهی مقدار متغیر را تغییر نمی دهد (که ما از این به بعد مقدار اولیه می نامیمش initialValue )

/* this is our initial value i.e. the starting point*/
const initialValue = 0;

/* numbers array */
const numbers = [5, 10, 15];

/* reducer method that takes in the accumulator and next item */
const reducer = (accumulator, item) => {
  return accumulator + item;
};

/* we give the reduce method our reducer function
  and our initial value */
const total = numbers.reduce(reducer, initialValue)

ممکن کد فوق یه مقدار گیج کننده باشه، اما در اصل جادویی وجود نداره. بیاید تا console.log را به متد reducer اضافه کنیم که آرگومان های accumulator و item را خروجی می دهد.

خروجی به صورت زیر خواهد بود:

اولین چیزی که باید به آن توجه کرد این هست که متد ما 3 مرتبه فراخوانی شده است، چرا که داخل آرایه ما سه مقدار داشتیم. accumulator مان با صفر (۰) شروع می کند که مقدار initialValue مان می باشد و برای reduce می فرستیمش. آخرین فراخوانی متد مقدار accumulator آن برابر با ۱۵ می باشد و item نیز ۱۵ است که ۱۵+۱۵ به ما خروجی ۳۰ را می دهد و مقدار نهایی می باشد. به یاد داشته باشید که متد reducer مقدار accumulator را بعلاوه item باز می گرداند.

بنابراین مثال ساده ای برای چگونگی استفاده از reduce را داشتیم. حال بیاید به مثال های پیجیده تر بپردازیم.

مسطح کردن یک آرایه با استفاده از Reduce

خب اول از همه فرض کنید آرایه زیر را داریم:

const numArray = [1, 2, [3, 10, [11, 12]], [1, 2, [3, 4]], 5, 6];

و فرض کنید که به هر دلیل دیوانه کننده ای، JavaScript متد .flat را حذف کرده و ما باید خودمان این آرایه را مسطح کنیم.

بنابراین ما باید تابعی برای مسطح کردن هر آرایه ای بدون در نظر گرفتن مقدار عمق تودرتویی آن، خودمان بنویسیم:

function flattenArray(data) {
  // our initial value this time is a blank array
  const initialValue = [];

  // call reduce on our data
  return data.reduce((total, value) => {
    // if the value is an array then recursively call reduce
    // if the value is not an array then just concat our value
    return total.concat(Array.isArray(value) ? flattenArray(value) : value);
  }, initialValue);
}

اگر آرایه فرض شده در اول را به متد بدهیم و نتایج را لاک بگیریم، به صورت زیر خواهد بود:

یه مثال خیلی خوب برای ساده کردن کار توسط Reduce را مشاهده کردید.

باز بیاید یه مثال دیگه ببینیم:

تغییر ساختار یک Object

با توجه به این که بازی Pokemonرایج شده، بیاید وانمود کنیم سروری داریم که مجموعه ای از اشیای Pokemon (مشابه زیر) را برای ما می فرستد:

const pokemon = [
  { name: "charmander", type: "fire" },
  { name: "squirtle", type: "water" },
  { name: "bulbasaur", type: "grass" }
]

ما نیاز داریم تا این شی را تغییر دهیم تا به شکل زیر شود:

const pokemonModified = {
  charmander: { type: "fire" },
  squirtle: { type: "water" },
  bulbasaur: { type: "grass" }
};

برای داشتن خروجی بالا، به صورت زیر زیر عمل می کنیم:

const getMapFromArray = data =>
  data.reduce((acc, item) => {
    // add object key to our object i.e. charmander: { type: 'water' }
    acc[item.name] = { type: item.type };
    return acc;
  }, {});

اگر متد را فراخوانی کنیم:

getMapFromArray(pokemon)

خروجی زیر را خواهیم داشت:

سینتکس reduceدر جاوااسکریپت

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

پارامترها:

callback : تابعی برای عمل کردن روی هر المان در آرایه (بجز مورد اول در حالتی که initialValue ارائه نشده باشه)

این تابع چهار آرکومان می گیرد:

  • accumulator : این‌آرگومان، مقادیر برگشتی را جمع می کند. مقدار انباشته شده در آخرین برگشت فراخوانی می باشد
  • current value‌ : المان فعلی از آرایه ای که در حال پردازش است.
  • index (دلخواه – Optional) : شاخص الملن فعلی که در آرایه در حال پردازش است. اگر مقدار اولیه تعریف شده باشد، از صفر شروع می شود، در غیر این صورت از یک شروع می شود.
  • array : آرایه ای که reduce روی آن عمل می کند.
  • initial value: مقداری که به عنوان آرگومان اول در فراخوانی اول استفاده می شود. اگر initial value تعریف نشده باشد، المان اول در آرایه به عنوان accumulator ابتدایی در نظر گرفته می شود و از روی current value رد می شود.

تعریف reduce :

متد reduce تابع callback را به ازای هر مقدار حاضر در آرایه یک بار اجرا می کند و ۴ آرگومان می گیرد:

  1. accumulator
  2. currentValue
  3. currentIndex
  4. array

اگر اولین callback صدا زده شود، accumulator و currentValue می توانند یکی از دو مقدار زیر را داشته باشند.

۱- اگر initialValue در فراخوانی reduce شامل شده باشد، سپس accumulator برابر با initialValue خواهد بود و currentValue مساوی با اولین مقدار آرایه خواهد بود.

۲- اگر initialValue شامل نشده باشد، سپس accumulator برابر با اولین مقدار آرایه خواهد بود و currentValue برابر با دومی خواهد بود.

نکته: اگر initialValue شامل نشده باشد، reduce() تابع callback را با شاخص ۱ شروع خواهد کرد (از روی اولین شاخص می گذرد، از روی NDEX شماره صفر آرایه می گذرد). اگر initialValue شامل شده باشد، از شاخص ۰ شروع خواهد کرد.

اکر آرایه خالی باشد و initialValue شامل نشده باشد، TypeError خواهیم داشت.

برای بررسی بهتر به REDUCE زیر توجه داشته باشید:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue
})

callback چهار دفعه فراخوانی می شود، در هر بار فراخوانی آرگومان ها و مقدار برگشتی به صورت زیر می باشد:

همچنین به جای استفاده از full functionمی توانید از arrow functionاستفاده کنید:

[0, 1, 2, 3, 4].reduce( (accumulator, currentValue, currentIndex, array) => accumulator + currentValue )

اگر مقدار initial value در نظر گرفته باشید:

[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => {
    return accumulator + currentValue
}, 10)

نتیجه گیری

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