תקן ES2019 כולל בחובו כמה תוספות מעניינות ולא דרמטיות במיוחד – התוספת המעניינת ביותר היא flatMap שלמרות שהיא לא מהפכנית היא שימושית במיוחד בלא מעט הזדמנויות.
אז מה היא עושה? בואו ונעבור על map. שהיא מתודה של מערך בג'אווהסקריפט. איך עובד map, אני מריץ אותו כשאני רוצה לעבור על כל אברי המערך ולהחזיר מערך בגודל זהה עם מוטציה (מוטציה – או Mutation זה לשנות את איברי המערך). למשל:
const myArray = ['Ran', 'Moshe', 'Yaakov', 'Yossi'];
const result = myArray.map((name) => {
return `${name} is nice!`
});
console.log(result);
//["Ran is nice!", "Moshe is nice!", "Yaakov is nice!", "Yossi is nice!"]
מה יש לי פה? מערך של שמות שעליהם אני מפעיל פונקצית map. הפונקציה הזו היא פונקציה שיש אותה לכל המערכים והיא מקבלת פונקציה אנונימית (במקרה הזה פונקצית חץ – זוכרים? אם לא – קראו פה) שמחזירה את השם לאחר מוטציה פשוטה שמוסיפה לו טקסט.
אבל כשאנחנו עושים מוטציות מורכבות, אנחנו מתחילים להכנס לעולם חדש של כאב. בואו וניקח למשל אתר של מתכונים. אני בוחר מתכון והרכיבים שלו שהם מערך, מתווספים לרשימה. אני רוצה שבלחיצת כפתור כל רכיבי המתכונים יתווספו לרשימת קניות. אם המשתמש שלי בחר שני מתכונים, יש לי מערך שמונה שני איברים. באיבר הראשון הרכיבים של מתכון א' ובאיבר השני רכיבים של מתכון ב':
['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar']
אני רוצה להגיע למצב שבו יש:
["Carrot", "Cabbage", "Salt", "Mayonnaise", "Bread", "Oil", "Eggs", "Cream", "Sugar"]
איך עושים את זה? הדבר הראשון שקופץ לנו הוא map, לעבור על כל המערך ולבצע split כדי לבודד את כל הרכיבים. מה הבעיה? שבסוף יש לי שני מערכים. map מתמפה בדיוק למספר האיברים המקורי. היו לי שני איברים בהתחלה? יהיו לי גם שני איברים בסוף.
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
const result = myArray.map((name) => {
return name.split(',');
});
console.log(result);
//[["Carrot Cabbage Salt Mayonnaise"], ["Bread Oil Eggs Cream Sugar"]]
וזה מאוד מבאס, כי אני רוצה מערך אחד שלם שיש בו את כל הרכיבים. מה עושיןם? אפשר להשתמש ב-flat על המערך או… להשתמש פשוט ב-flatMap:
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
const result = myArray.flatMap((name) => {
return name.split(',');
});
console.log(result);
//["Carrot", "Cabbage", "Salt", "Mayonnaise", "Bread", "Oil", "Eggs", "Cream", "Sugar"]
אלגנטי מאוד. זה פשוט חוסך שימוש ב-flat. כיוון ש-flatMap לא מחויב להחזיר לנו אותו מספר של איברים, אפשר להשתמש בו גם למוטציה וגם לפילטור. למשל, בואו נעמיד פנים שאני טוקבקיסט מגעיל מ-Ynet ואני נדרש לממש אלגוריתם של תגובות שמקבל מערך של גילאים. כל מה שהוא מתחת לגיל 35 אני כותב שהוא מתאים להייטק וכל מה שהוא מעל גיל 35 אני מעיף אותו החוצה. איך אני עושה את זה?
אם אני משתמש ב-map, אני מקבל תוצאה לא צפויה:
const highTechWorkers = ['20', '25', '41', '9'];
const result = highTechWorkers.map((worker) => {
const age = parseInt(worker);
if (age < 35)
return `age ${worker} is OK`;
});
console.log(result);
//["age 20 is OK", "age 25 is OK", undefined, "age 9 is OK"]
למה? כי map מחזיר בדיוק את אותם ערכים כמו במערך המקורי. לא החזרתי? יהיה לי מערך עם אותם איברים ובמקרים שלא החזרתי אני מקבל undefined. אם אני רוצה לפלטר, אני צריך להפעיל את פונקצית פילטר ואז את פונקצית map:
const highTechWorkers = ['20', '25', '41', '9'];
highTechWorkersFiltered = highTechWorkers.filter((worker) => {
const age = parseInt(worker);
if (age < 35) {
return true;
} else {
return false;
}
});
const result = highTechWorkersFiltered.map((worker) => {
return `age ${worker} is OK`;
});
console.log(result);
//["age 20 is OK", "age 25 is OK", "age 9 is OK"]
אני יכול לעשות את זה יותר אלגנטי כמובן, אבל עדיין זה filter ואז map. אבל flatMap יכול לחסוך לי את כאב הראש כי הוא לא חייב להחזיר את אותם איברים כמו map ובניגוד ל-filter, הוא מבצע מוטציה במערך. איך? כל מה שאני רוצה? אני אחזיר בתוך מערך בודד. כל מה שאני לא רוצה? אני אחזיר מערך ריק. ה-flatMap לוקח את המערך שיצרתי, מעיף את כל המערכים הריקים והמערכים המלאים מתפשטים מהמערכים שלהם. הכי כיף שיש:
const highTechWorkers = ['20', '25', '41', '9'];
const result = highTechWorkers.flatMap((worker) => {
const age = parseInt(worker);
if (age < 35) {
return [`age ${worker} is OK`]; // Keep it, in array
} else {
return []; // Delete it!
}
});
console.log(result);
//["age 20 is OK", "age 25 is OK", "age 9 is OK"]
ואם זה נראה לכם מורכב מדי? שחקו עם זה. אבל שיהיה לכם בראש: אתם צריכים לעשות מוטציה למערך וגם לפלטר אותו? במקום לגשת ל-filter ואז ל-map. או לאלטרנטיבות אחרות כמו reduce למשל – כדאי לחשוב על flatMap. לא חידוש בקנה מידה מהפכני וחדשני, אבל ללא ספק חשוב שיהיה בשפה.
8 תגובות
נראה לי ששימוש ב
-FlatMap
לפילטור בדוגמה שלך הופך את הקוד ללא קריא, וכמובן כל ייצור המערכים המיותרים נראה לי כבזבוז משאבים לא יעיל.
לעומת זאת כשאני משתמש במסנן ואחר כך במיפוי – ברור מה הפונקציה עושה.
const highTechWorkers = ['20', '25', '41', '9'];
const result = highTechWorkers
.filter(age => +age `age ${worker} is OK`)
המטרה בדוגמאות היא לאו דווקא להראות יעילות אלא להדגים. מן הסתם ליצור מערכים רבים זה משהו שלא נעשה בקוד פרודקשן.
הדוגמה שנתת לא ברורה כל כך, תוכל לבדוק אותה שוב? 🙂
יש לך שגיאה כאן:
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
const result = myArray.map((name) => {
return name.split(',');
});
console.log(result);
//[["Carrot Cabbage Salt Mayonnaise"], ["Bread Oil Eggs Cream Sugar"]]
התוצאה היא מערך עם 2 אברים, שכל איבר הוא מערך של שמות מצרכים.
אתה כתבת שבכל איבר יש סטרינג.
תודה על התגובה, אבל איפה כתבתי שבל איבר יש סטרינג? יש משהו שאני מפספם? 🙂
למה לא להשתמש ב join ו split ?
const myArray = ['Carrot,Cabbage,Salt,Mayonnaise', 'Bread,Oil,Eggs,Cream,Sugar'];
var b=myArray.join(',').split(',')
console.log(b)
[ 'Carrot',
'Cabbage',
'Salt',
'Mayonnaise',
'Bread',
'Oil',
'Eggs',
'Cream',
'Sugar' ]
הפונקציה הזו לא עובדת בנוד?
ובכלל לאיזה ES נוד מעודכנת?
או קי מצאתי את זה
https://node.green/#ES2019
שם מפורטים החידושים של JS באיזה גירסאות של נוד הם מעודכנות
ומסתבר שבנוד 12 זה מעודכן
רק שאני מפחד לעדכן ל 12 שלא ישבור לי דברים בפרויקטים קודמים
myArray.join().split(',')
יותר אלגנטי