Menu

מתודות מערך ב-JavaScript: map, filter, reduce ועוד

מתודות המערך שמחליפות את רוב לולאות ה-for שלכם: map, filter, reduce, find, some ו-every, ואילו מהן משנות את המערך ואילו מחזירות מערך חדש.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

למערכים יש ארגז כלים

למערכים ב-JavaScript יש אוסף גדול של מתודות מובנות. רוב מה שהייתם כותבים בשבילו לולאת for, כמו שינוי ערכים, בחירה של חלק מהם או סכימה, כבר קיים כמתודה שעושה את זה בשורה אחת, נקראת טוב יותר ומשתלבת בקלות עם מתודות אחרות.

ערכת הבסיס היא שלוש מתודות: map, filter ו-reduce. למדו אותן וכמה מבני הדודים שלהן, והקוד העמוס בלולאות שלכם יתכווץ למשהו שאפשר לסרוק במבט אחד.

כל מתודה מקבלת callback ומחזירה משהו. אף אחת מהן לא שינתה את nums, וזה עיקרון שכדאי להפנים מוקדם.

map: שינוי של כל איבר

map מקבלת פונקציה וקוראת לה על כל איבר, ואוספת את ערכי ההחזרה למערך חדש באותו אורך. השתמשו בה כשאתם רוצים "פלט אחד לכל קלט".

ה-callback מקבל גם את האינדקס כארגומנט שני, אם צריך אותו: arr.map((item, i) => ...). כשלא צריך, פשוט מתעלמים ממנו.

טעות נפוצה: לבחור ב-map כשלא צריכים את המערך שהיא מחזירה. אם אתם רק רוצים להדפיס כל פריט או לשמור אותו במסד נתונים, זה תפקיד של forEach או של לולאה רגילה.

filter: שמירה של מה שמתאים

filter קוראת לפרדיקט, פונקציה שמחזירה true או false, על כל איבר, ושומרת את האיברים שעבורם התוצאה truthy. המערך החדש באותו אורך או קצר יותר.

map ו-filter משתרשרות באופן טבעי. קראו את השרשרת משמאל לימין כמו צינור:

קודם מסננים, אחר כך ממפים: כך map רצה רק על מי ששרד.

reduce: קיפול מערך לערך אחד

reduce היא הכללית מבין השלוש. נותנים לה פונקציית reducer (accumulator, item) => newAccumulator וערך התחלתי. היא עוברת על המערך, מעבירה כל פריט ל-reducer יחד עם הצובר עד כה, ומחזירה את מה שהצובר הפך אליו בסוף.

התוצאה לא חייבת להיות מספר. היא יכולה להיות אובייקט, מערך אחר, מחרוזת, כל דבר שבונים בהדרגה:

תמיד העבירו את הערך ההתחלתי (הארגומנט השני). בלעדיו reduce משתמשת באיבר הראשון כצובר ההתחלתי, מה שנשבר על מערכים ריקים ולעיתים קרובות גם לא עושה את מה שרציתם.

reduce חזקה, אבל יכולה להיות קשה לקריאה כשהלוגיקה מסתבכת. אם ה-reducer שלכם ארוך מכמה שורות, לולאת for...of רגילה היא לרוב ברורה יותר.

forEach: תופעות לוואי, בלי ערך החזרה

forEach היא כמו map בלי המערך המוחזר. היא קיימת למקרים שבהם רוצים לעשות משהו עם כל פריט, להדפיס אותו, לקרוא ל-API, לעדכן את ה-DOM, ולא צריכים אוסף חדש.

שני דברים שכדאי לדעת:

  • forEach מחזירה undefined. אי אפשר לשרשר אחריה .map().
  • אי אפשר לצאת מ-forEach מוקדם עם break. אם צריך יציאה מוקדמת, השתמשו ב-for...of או ב-some/every.

אם אתם מוצאים את עצמכם כותבים arr.forEach(x => results.push(transform(x))), זה בעצם map.

find ו-findIndex: רק אחד, בבקשה

find מחזירה את האיבר הראשון שמתאים לפרדיקט, או undefined אם אין התאמה. findIndex מחזירה את האינדקס (או -1).

find עוצרת בהתאמה הראשונה. אל תשתמשו ב-filter(...)[0]: הוא סורק את כל המערך רק כדי לזרוק את השאר.

some ו-every: שאלות של כן או לא

some מחזירה true אם לפחות איבר אחד עובר את הבדיקה. every מחזירה true רק אם כולם עוברים.

שתיהן עוצרות מוקדם: some עוצרת ב-true הראשון, every עוצרת ב-false הראשון. הן הכלי הנכון לשאלות מסוג "האם יש…" ו"האם כולם…".

slice מול splice: העתקה מול חיתוך

השתיים נשמעות דומות ועושות דברים שונים מאוד.

slice(start, end) מחזירה עותק רדוד של חלק מהמערך. היא לא משנה כלום. end לא נכלל; השמיטו אותו כדי להגיע עד הסוף.

splice(start, deleteCount, ...items) משנה את המערך במקום. היא מסירה deleteCount איברים החל מ-start, מכניסה איברים חדשים אם רוצים, ומחזירה את האיברים שהוסרו.

דרך לזכור: slice בטוחה (מעתיקה), splice מנתחת את המערך במקום.

משנות מול לא משנות

ההבחנה הזו חשובה. קוד שמשנה בטעות מערך משותף הוא אחד הבאגים המעצבנים יותר לאיתור.

משנות (משנות את המקור, ובדרך כלל מחזירות משהו אחר):

  • push, pop, shift, unshift
  • splice, sort, reverse
  • fill, copyWithin

לא משנות (מחזירות מערך או ערך חדש, המקור לא נפגע):

  • map, filter, slice, concat
  • flat, flatMap
  • find, findIndex, some, every, includes, indexOf
  • reduce, reduceRight

השתיים שצריך לשים עליהן עין הן sort ו-reverse: הן נראות תמימות ומשנות את המערך בשקט. אם צריך עותק ממוין, הפעילו קודם slice:

ב-JS מודרני יש גם תאומות שלא משנות: toSorted, toReversed, toSpliced ו-with. הן מחזירות מערכים חדשים ומשאירות את המקור כמו שהוא. הן נתמכות בכל סביבת ריצה עדכנית, אז השתמשו בהן כשהן זמינות.

flat ו-flatMap

flat משטחת מערכים מקוננים ברמה אחת (או יותר, עם ארגומנט עומק). flatMap היא map ואחריה flat ברמה אחת: שימושית כשכל פריט ממופה לאפס פלטים או יותר.

flatMap היא הדרך הנקייה "לפרוס" פריטים, קלט אחד והרבה פלטים, בלי שלב flat() נוסף.

לחבר הכול יחד

דוגמה קטנה ומציאותית. בהינתן רשימת הזמנות, נחשב את סך ההכנסות מהזמנות שהושלמו ושסכומן מעל $50:

שלוש מתודות, צינור אחד, בלי ניהול לולאה. כל שלב מתאר מה הוא עושה. אפשר לאחד את שתי קריאות ה-filter, אבל הפיצול נקרא טוב ולפעמים עוזר כשמדבגים.

הבא בתור: Map ו-Set

מערכים מטפלים היטב ברצפים מסודרים, אבל הם מסורבלים כשצריך חיפוש מהיר לפי מפתח או אוסף של ערכים ייחודיים. ל-JavaScript יש שני מבני נתונים מובנים בדיוק לתפקידים האלה, Map ו-Set, והם העמוד הבא.

שאלות נפוצות

מה ההבדל בין map, filter ו-reduce ב-JavaScript?

map משנה כל איבר ומחזירה מערך חדש באותו אורך. filter שומרת רק את האיברים שעוברים בדיקה ומחזירה מערך חדש (בדרך כלל קצר יותר). reduce עוברת על המערך ומקפלת אותו לערך יחיד: סכום, אובייקט, מערך אחר, כל מה שבונים בדרך.

מה ההבדל בין forEach ל-map?

forEach מריצה פונקציה על כל איבר ומחזירה undefined: היא נועדה לתופעות לוואי. map מריצה פונקציה על כל איבר ומחזירה מערך חדש של התוצאות. אם אתם צריכים מערך אחרי שינוי, השתמשו ב-map. אם אתם רק רוצים לעשות משהו עם כל איבר ולא צריכים את התוצאה, השתמשו ב-forEach (או בלולאת for...of).

אילו מתודות מערך משנות את המערך המקורי?

המתודות המשנות הן push, pop, shift, unshift, splice, sort, reverse, fill ו-copyWithin. כל השאר, map, filter, slice, concat, flat, flatMap, find, some, every, reduce, משאירות את המערך המקורי כמו שהוא ומחזירות ערך חדש.

מתי להשתמש ב-slice ומתי ב-splice?

slice(start, end) מחזירה עותק רדוד של קטע מהמערך ולא נוגעת במקור. splice(start, deleteCount, ...items) משנה את המערך: היא מסירה ו/או מכניסה איברים במקום ומחזירה את האיברים שהוסרו. דרך לזכור: slice בטוחה, splice מנתחת את המערך עצמו.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל