مقالات طراحی سایت

آموزش موارد مربوط به طراحی و ساخت وب سایت

اگر نیاز نیست تا وب سایت شما با مرورگرهای قدیمی مانند IE8 سازگار باشد بهتر است با متدهای .map()، .reduce()، و .filter() آشنا شوید. البته اگر شما با زبان برنامه نویسی دیگری غیر از جاوااسکریپت کار میکنید این مقاله میتواند برای شما مفید باشد، زیرا بیشتر زبان های برنامه نویسی این مفاهیم را در خود دارند.

.map()

اجازه بدهید در قالب یک مثال نحوه عملکرد این متد را به شما نشان بدهم. تصور کنید آرایه ای از چندین آبجکت دارید - هر آبجکت نماینده یک فرد است. اما شما به id هر فرد نیاز دارید.

 
// What you have
var officers = [
 { id: 20, name: 'Captain Piett' },
 { id: 24, name: 'General Veers' },
 { id: 56, name: 'Admiral Ozzel' },
 { id: 88, name: 'Commander Jerjerrod' }
];
// What you need
[20, 24, 56, 88]
 

شیوه های مختلفی برای اینکار وجود دارد. میتوانید یک آرایه خالی تعریف کنید و بعد از .forEach()، .for(...of)، و یا .for() استفاده کنید.

بیایید مقایسه کنیم:

با استفاده از .forEach():

 
var officersIds = [];
officers.forEach(function (officer) {
 officersIds.push(officer.id);
});
 

همانطور که ملاحظه کردید شما مجبور هستید تا یک آرایه خالی ایجاد کنید. حالا با .map() اینکار را انجام میدهیم:

 
var officersIds = officers.map(function (officer) {
 return officer.id
});
 

با کمک arrow function کد زیباتری خواهیم داشت:

 
const officersIds = officers.map(officer => officer.id);
 

مطالعه بیشتر: نه ویژگی ES6 که هر جاوااسکریپت کاری باید بداند

اما .map() چگونه کار میکند؟ بطور کلی .map() دو آرگومان، یک callback، و یک context اختیاری میگیرد (که میتواند در callback معادل this درنظر گرفته شود). callback برای هر مقدار موجود در آرایه اجرا میشود و تک تک مقادیر جدید را در آرایه نتیجه بازگرداند.

درنظر داشته باشد که طول آرایه نتیجه برابر با طول آرایه اصلی می باشد.

.reduce()

.reduce() هم مانند .map()، برای هر المان آرایه یک callback اجرا میکند. تفاوت در اینجاست که .reduce() نتیجه این callback را (accumulator) از یک المان آرایه به المان دیگر پاس میدهد.

accumulator میتواند هر چیزی باشد (عدد، رشته، آبجکت، ...) و باید زمانی که .reduce() فراخوانی میشود نمونه سازی و یا پاس داده شود.

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

 
var pilots = [
 {
   id: 10,
   name: "Poe Dameron",
   years: 14,
 },
 {
   id: 2,
   name: "Temmin 'Snap' Wexley",
   years: 30,
 },
 {
   id: 41,
   name: "Tallissan Lintra",
   years: 16,
 },
 {
   id: 99,
   name: "Ello Asty",
   years: 22,
 }
];
 

ما سالهای خدمت همه این خلبان ها را میخواهیم. با کمک .reduce() به راحتی میتوانیم این عدد را محاسبه کنیم:

 
var totalYears = pilots.reduce(function (accumulator, pilot) {
 return accumulator + pilot.years;
}, 0);
 

توجه داشته باشید که من مقدار شروع را برابر با 0 در نظر گرفتم. همچنین اگر لازم شد میتوانم از این متغیر استفاده کنم. پس از فراخوانی callback برای هر المان آرایه، .reduce() مقدار نهایی accumulator را (که در اینجا برابر با 82 است) بر میگرداند.

با کمک arrow function کد زیباتری خواهیم داشت:

 
var mostExpPilot = pilots.reduce(function (oldest, pilot) {
 return (oldest.years || 0) > pilot.years ? oldest : pilot;
}, {});
 

اگر قرار باشد باتجربه ترین خلبان را پیدا کنیم باید چکار کنیم؟

 
var mostExpPilot = pilots.reduce(function (oldest, pilot) {
 return (oldest.years || 0) > pilot.years ? oldest : pilot;
}, {});
 

من نام accumulator را oldest قرار دادم. callbackهای من accumulator را با هر خلبان مقایسه میکنند. اگر خلبانی تجربه کاری بیش از oldest داشته باشد، پس آن خلبان oldest جدید خواهد شد و بنابراین آنرا return خواهم کرد.

همانطور که ملاحظه میکنید، با کمک .reduce() خیلی راحت میتوانید مقدار و یا آبجکت از آرایه تولید کنید.

.filter()

اما اگر تنها بعضی المان ها را از یک آرایه بخواهید چکار میکنید؟ در این شرایط شما میتوانید از .filter() استفاده کنید.

دیتا شما به این صورت است:

 
var pilots = [
 {
   id: 2,
   name: "Wedge Antilles",
   faction: "Rebels",
 },
 {
   id: 8,
   name: "Ciena Ree",
   faction: "Empire",
 },
 {
   id: 40,
   name: "Iden Versio",
   faction: "Empire",
 },
 {
   id: 66,
   name: "Thane Kyrell",
   faction: "Rebels",
 }
];
 

از شما خواسته شده دو آرایه ایجاد کنید: آنهایی که Empire هستند، و آنهایی که rebel هستند. خوب با کمک .filter() به راحتی میتوانید اینکار را انجام دهید.

 
var rebels = pilots.filter(function (pilot) {
 return pilot.faction === "Rebels";
});
var empire = pilots.filter(function (pilot) {
 return pilot.faction === "Empire";
});
 

با کمک arrow function کد زیباتری خواهیم داشت:

 
const rebels = pilots.filter(pilot => pilot.faction === "Rebels");
const empire = pilots.filter(pilot => pilot.faction === "Empire");
 

در کل اگر فانکشن callback مقدار true را برگرداند، آن المان به آرایه خروجی اضافه خواهد شد و اگر مقدار false را برگرداند، اضافه نخواهد شد.

ترکیب .map()، .reduce()، و .filter()

از آنجایی که هر سه آرایه فراخوانی میکنند و .map() و .filter() هردو آرایه برمیگردانند، میتوانیم فراخوانی هایمان را همزمان با هم انجام بدهیم.

بیایید در یک مثال باهم بررسی کنیم. دیتا بصورت زیر است:

 
var personnel = [
 {
   id: 5,
   name: "Luke Skywalker",
   pilotingScore: 98,
   shootingScore: 56,
   isForceUser: true,
 },
 {
   id: 82,
   name: "Sabine Wren",
   pilotingScore: 73,
   shootingScore: 99,
   isForceUser: false,
 },
 {
   id: 22,
   name: "Zeb Orellios",
   pilotingScore: 20,
   shootingScore: 59,
   isForceUser: false,
 },
 {
   id: 15,
   name: "Ezra Bridger",
   pilotingScore: 43,
   shootingScore: 67,
   isForceUser: true,
 },
 {
   id: 11,
   name: "Caleb Dume",
   pilotingScore: 71,
   shootingScore: 85,
   isForceUser: true,
 },
];
 

ما قصد داریم امتیاز نهای کاربران force را بدست آوریم. بیایید اینکار را گام به گام انجام دهیم:

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

 
var jediPersonnel = personnel.filter(function (person) {
 return person.isForceUser;
});
// Result: [{...}, {...}, {...}] (Luke, Ezra and Caleb)
 

اکنون 3 المان در آرایه نتیجه وجود دارد. اکنون باید آرایه ای ایجاد میکنیم که شامل مجموع امتیازات هر Jedi باشد.

 
var jediScores = jediPersonnel.map(function (jedi) {
 return jedi.pilotingScore + jedi.shootingScore;
});
// Result: [154, 110, 156]
 

برای رسیدن به مجموع امتیازات از .reduce() استفاده میکنیم.

 
var totalJediScore = jediScores.reduce(function (acc, score) {
 return acc + score;
}, 0);
// Result: 420
 

قسمت جذاب این است که میتوانیم همه اینها را بصورت زنجیروار کنار هم قرار دهیم:

 
var totalJediScore = personnel
 .filter(function (person) {
 return person.isForceUser;
 })
 .map(function (jedi) {
 return jedi.pilotingScore + jedi.shootingScore;
 })
 .reduce(function (acc, score) {
 return acc + score;
 }, 0);
 

با کمک arrow function کد زیباتری خواهیم داشت:

 
const totalJediScore = personnel
 .filter(person => person.isForceUser)
 .map(jedi => jedi.pilotingScore + jedi.shootingScore)
 .reduce((acc, score) => acc + score, 0);
 

البته برای مثال بالا لازم نیست از .map() و .filter() استفاده کرد و تنها با .reduce() میشد مساله را حل کرد. من فقط اینکار را انجام دادم تا استفاده همزمان از این سه متد را ببینید.

 
const totalJediScore = personnel.reduce((acc, person) => 
  person.isForceUser ? acc + person.pilotingScore + person.shootingScore : acc, 0
);
 

چرا از .forEach() استفاده نکنیم؟

فرض کنید  لیستی از افراد که شمال نام و شغل آنها میشود را دارید.

 
var data = [
 {
   name: "Jan Dodonna",
   title: "General",
 },
 {
   name: "Gial Ackbar",
   title: "Admiral",
 },
]
 

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

 
var result = formatElement([element])[0];
// Yeah... that's not right at all
 

که اصلا امکان پذیر نیست، و یا حلقه باید دور فانکشن قرار بگیرد، مثل:

 
data.forEach(function (element) {
 var formatted = formatElement(element);
 // But what then....
});
 

اما .forEach() چیزی return نمیکند. این بدان معناست که شما باید نتایج را درون یک آرایه push کنید.

 
var results = [];
data.forEach(function (element) {
 var formatted = formatElement(element);
 results.push(formatted);
});
 

درنتیجه، شما دو فانکشن دارید: فانکشن formatElement() و فانکشنی که نتایج را به آرایه push میکند.

چرا باید دو فانکشن داشته باشیم درحالی که میتوانیم با یک فانکشن آن کار را انجام دهیم؟

 
var results = data.map(formatElement);
 

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

به نقل از: medium

 
نوشتن دیدگاه