באמצעות המתודות filter, map ו- reduce נוכל לבצע פעולות שונות על מערכים, במאמר זה נעבור על אחת מהן ונבאר את אופן פעולתן
במאמר זה נבאר 3 מתודות חשובות אשר יכולות לעזור לנו בסיטואציות מסוימות.
במאמר מניפולציות על מערכים עברנו על מתודות שניתן להיעזר בהם על מנת לבצע פעולות שונות על מערכים,
גם עסקנו בשאלה האם הן דיסטרקטיביות, אך למתודות שבהן נעסוק היום יש מטרות אחרות.
()filter
זוהי בעצם מתודת חיפוש, כאשר היא פועלת בהתאם לתנאי שניתן לה.
filter היא מתודה Non-Destructive, כלומר שהיא לא תשנה את המערך המקורי אלא תחזיר לנו חדש.
כפרמטר, פונקציה זו מקבלת פונקציית callBack אשר בתוכה יתקיים התנאי וההחזר יהיה בהתאם.
נתאר לנו מצב שבו נרצה לדלות מתוך רשימה רק את הערכים שנגמרים באות e.
ניצור לשם כך פונקציה שתקבל כל ערך מהמערך ותחזיר אותו למערך החדש רק אם הוא נגמר ב-e.
const colors = ['Blue', 'Yellow', 'Red', 'Green', 'Purple', 'Orange']; let filteredColors = colors.filter(endsWithE); function endsWithE(item) { return item[item.length-1] === 'e' } console.log(`filteredColors: ${filteredColors}`) // filtered array - ['Blue','Purple','Orange'] console.log(`colors: ${colors}`)// original array - ['Blue', 'Yellow', 'Red', 'Green', 'Purple', 'Orange']
ולכן נוכל לקבל את התוצאה הבאה:
()map
במתודה זו נוכל להשתמש על מנת להחזיר מערך חדש לאחר שינוי.
זה יכול להזכיר מעט את filter אך עקרון הפעולה הוא שונה למעשה.
גם למתודה זו נשלח כפרמטר פונקציית callBack, רק שכאן זה עובד קצת אחרת –
המערך החדש שיחזור לנו יהיה בעל אותו מספר האיברים כמו המערך הקודם,
כלומר שמה שאנחנו משנים כאן בעצם זה את הערכים עצמם ולא את מספר הערכים.
נוכל לבאר זאת באמצעות ה-Code Snippet הבא:
const numbers = [1,2,3,4,5]; let myNumbers = numbers.map(item => { return item *= item; }) console.log(`myNumbers: ${myNumbers}`) // new array - [1,4,9,16,25] console.log(`numbers: ${numbers}`)// original array - [1,2,3,4,5]
* בדוגמא זו בשונה ממה שעשינו ב-filter, במקום לשלוח פונקציה כתבנו פונקציה אנונימית באמצעות fat arrow (<=).
כך שמה שעשינו כאן בעצם, זה להחזיר מערך חדש שמורכב מהחזקות של כל ערך מהמערך המקורי,
ולכן, אם נריץ את התוכנית נוכל לקבל את התוצאה הבאה:
()reduce
באמצעות המתודה reduce נוכל לבצע פעולת צמצום למערך,
כלומר שבאמצעות ביצוע פעולה שנגדיר בעזרת פונקציית callback על כל אחד מן האיברים שלו,
מה שמתודה זו תחזיר לנו זה את הערך הסופי של החישוב שבוצע.
זה אכן מבלבל מעט בהתחלה אך מיד נבאר את הנושא.
למתודה זו יש 3 העמסות שנוכל להשתמש בהן לצרכים שונים:
reduce((previousValue, currentValue, currentIndex, array) => {}, initialValue);
reduce((previousValue, currentValue, currentIndex) => {}, initialValue);
reduce((previousValue, currentValue) => {}, initialValue);
- המתודה reduce מקבלת פונקציית callBack כפרמטר.
- previousValue – זהו הערך מהקריאה הקודמת לפונקציית ה-callback.
במידה וכך הגדרנו – אז בקריאה הראשונה הוא יקבל את הערך של initialValue, אחרת יקבל את האיבר הראשון של המערך. - currentValue – הערך של האלמנט הנוכחי במערך, בקריאה הראשונה ערכו יהיה שווה לערך האלמנט השני של המערך,
במידה והגדרנו initialValue ערכו יהיה שווה לזה של האלמנט הראשון. - currentIndex – ערכו הוא האינדקס של currentValue במערך.
בקריאה הראשונה ערכו יהיה 1, אם נגדיר initialValue אז ערכו יהיה שווה ל-0.
דוגמא ראשונה בסיסית תהיה פשוט להחזיר את סכום הערכים של המערך הנתון:
const numbers = [1, 2, 3, 4, 5]; let sum = numbers.reduce((prev, curr) => { return prev + curr; }) console.log(`myNumbers: ${sum}`) // sum - 15 console.log(`numbers: ${numbers}`)// original array - [1,2,3,4,5]
התוצאה שתתקבל אם כך תהיה 15, ואם נפרק זאת לגורמים נוכל להסביר זאת כך:
- prev – הוא הערך המוחזר מהקריאה הקודמת לפונקציית ה-callBack.
- curr – הוא הערך המוחזר מהקריאה הנוכחית לפונקציית ה-callBack.
- נוכל לראות את פונקציית ה-callBack כלולאה אשר מספר האיטרציות שהיא מבצעת – זהה למספר הערכים במערך.
- כך שבכל איטרציה ערכו של prev עולה משום שאנו מבצעים עליו פעולת חיבור לערכו של האיבר הנוכחי.
- ולכן מה שקרה מאחורי הקלעים זה שבוצעה לולאה שבכל פעם העלתה את הערך של sum – עד אשר הגענו לסכום הסופי.
()reduce – דוגמאות נוספות
וכעת נדגים ונבאר דוגמאות מורכבות יותר.
תארו לכם מצב שבו נרצה לבדוק אם כל הערכים של מערך נתון הם שווים.
מה שנצטרך לעשות זה לבדוק בכל איטרציה אם הערך של prev אינו זהה ל-curr:
const numbers1 = [1, 1, 1, 4, 1]; const numbers2 = [1, 1, 1, 1, 1]; function isUniform(arr) { let answer = arr.reduce((prev, curr) => prev == curr) == true; return answer; } console.log(`numbers1 is uniform: ${isUniform(numbers1)}`) // false console.log(`numbers2 is uniform: ${isUniform(numbers2)}`) // true
כך שכאן מעולם לא שינינו את prev, אלא פשוט בדקנו אם curr זהה לו.
באופן דיפולטיבי יחזור לנו true בכל איטרציה,
false יוחזר רק אם התנאי לא יהיה נכון (כזב).
דוגמא טובה נוספת תהיה להחזיר את הערך הכי גבוה במערך:
const numbers1 = [1, 2, 3, 4, 5]; const numbers2 = [700, 60, -15, 407, 66]; function max(arr) { let max = arr.reduce((prev, curr) => prev > curr ? prev : curr); return max; } console.log(`numbers1 max: ${max(numbers1)}`) // 5 console.log(`numbers2 max: ${max(numbers2)}`) // 700
מה שעשינו כאן בעצם זה משפט תנאי מקוצר אשר בודק איזה איבר הכי גדול במערך כאשר אופן הפעולה הוא כדלקמן:
- בכל איטרציה, נבדוק אם prev גדול מ-curr או ההיפך.
- השתמשנו באופרטור Ternary שמחזיר לנו את prev אם התנאי אמת.
- curr יחזור במידה והוא בעל הערך הגדול ביניהם
- וכך max יקבל את הערך הגבוה ביותר מהמערך שלנו.
שילוב של filter, map ו- reduce
כמובן שנוכל להשתמש בשלושתן בבת אחת, כפונקציות אשר יש להן ערך החזר נוכל לשרשר אותן.
תארו לכם סיטואציה שבה ביקשו מאתנו לכתוב פונקציה אשר:
- מקבלת מערך
- מרכיבה ממנו מערך אשר הערכים שלו גדולים מ-5 בלבד
- כל ערך יוכפל בעצמו אם הוא כפולה של 5, אחרת הערך יוכפל ב-5.
- תחזיר לנו את הערך הגדול מהמערך החדש
אם כך, נשתמש ב-filter, map ו- reduce:
var arr = [1402, 3, 7, 400, 5012, 67, 3, 802, 91, 10]; function myfunc(arr) { return arr.filter(item => item >= 5). map(item => item % 5 == 0 ? item *= item : item *= 5). reduce((prev, curr) => prev > curr ? prev : curr); } console.log(myfunc(arr)); // 160000
כפי שניתן לראות בדוגמא זו:
- ביצענו את המיון באמצעות filter.
- הרכבנו את המערך החדש באמצעות map.
- צמצמנו את הטבלה לאיבר בעל הסכום הגדול ביותר באמצעות reduce.