חיפוש ]

מיון מערכים ב- JavaScript: מדריך למתחילים

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

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

התנהגות ברירת המחדל של ()Array.sort

כברירת מחדל, המתודה sort() ממיינת אלמנטים כמו מחרוזות בסדר עולה. מתודה זו עלולה להוביל לתוצאות בלתי צפויות בעבודה עם מספרים או סוגי נתונים מורכבים.

const numbers = [10, 5, 20, 2];
numbers.sort();
console.log(numbers); // Output: [10, 2, 20, 5] - incorrect for numeric sorting

בדוגמה מעלה, המתודה ממירה אלמנטים למחרוזות ומשווה את נקודות הקוד שלהם כ-Unicode, מה שמסביר מדוע "2" מגיע לפני "5", למרות שמספרית 2 קטן יותר.

כדי לתקן זאת עליכם לספק פונקציית השוואה (Compare Function). פונקציית השוואה ב-JavaScript היא חוק כלשהו שבאפשרותכם להגדיר כדי להנחות את המתודה sort() באופן בו עליה לסדר פריטים במערך.

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

יש לציין כי החל מ-ES2019, המתודה sort() מובטחת כ-stable sort בכל הדפדפנים המודרניים. כלומר, אלמנטים שנחשבים שווים על ידי פונקציית ההשוואה ישמרו על הסדר היחסי המקורי שלהם במערך.

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

מיון מספרים

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

const numbers = [10, 5, 20, 2];

// Ascending order
numbers.sort(function(a, b) {
    return a - b;
});
console.log(numbers); // Output: [2, 5, 10, 20]

// Descending order
numbers.sort(function(a, b) {
    return b - a;
});
console.log(numbers); // Output: [20, 10, 5, 2]

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

בדוגמה הראשונה (מיון בסדר עולה), a - b מבטיח שהמספרים הקטנים יבואו ראשונים. בדוגמה השנייה, b - a הופך את הסדר, וגורם למיון בסדר יורד.

ניתן גם לכתוב את אותה פונקציית השוואה בצורה מקוצרת יותר באמצעות Arrow Function:

// Ascending order with arrow function
numbers.sort((a, b) => a - b);

// Descending order with arrow function
numbers.sort((a, b) => b - a);

טיפ למתחילים: זכרו שמיון משנה את המערך המקורי. אם אתם צריכים לשמור את המערך המקורי כפי שהוא, עשו עותק באמצעות slice() או ה-Spread Operator (...) לפני המיון, או השתמשו במתודה toSorted() שמחזירה מערך ממוין חדש מבלי לשנות את המקורי.

מיון מחרוזות

עבור מחרוזות המתודה sort() עובדת היטב, אך לטיפול טוב יותר באותיות רישיות ותווים מיוחדים, השתמשו ב-localeCompare. שימוש ב-localeCompare מבטיח מיון תוך התחשבות במיקום ומטפל בווריאציות שונות כמו סימני ניקוד.

const fruits = ['Banana', 'apple', 'Cherry'];

// Sorting alphabetically using localeCompare
fruits.sort(function(a, b) {
    return a.localeCompare(b);
});
console.log(fruits); // Output: ['apple', 'Banana', 'Cherry']

Case-Insensitive Sorting

ניתן להתעלם מ"רגישות" לאותיות קטנות וגדולות על ידי העברת אפשרויות:

const cities = ['berlin', 'Amsterdam', 'Zurich', 'amsterdam'];

// Sorting alphabetically with case-insensitive comparison
cities.sort(function(a, b) {
    return a.localeCompare(b, undefined, { sensitivity: 'base' });
});
console.log(cities); // Output: ['Amsterdam', 'amsterdam', 'berlin', 'Zurich']

טיפ למתחילים: השתמשו ב-localeCompare או ב-Intl.Collator למיון מחרוזות שיעבוד כראוי בכל השפות. עבור רשימות גדולות, העדיפו את Intl.Collator לביצועים טובים יותר.

מיון מחרוזות מהיר יותר עם Intl.Collator

כאשר ממיינים מערכים גדולים של מחרוזות, Intl.Collator מציע ביצועים טובים משמעותית בהשוואה לקריאה ל-localeCompare() בכל השוואה.

ה-Collator מאתחל את הגדרות השפה פעם אחת ומשתמש בהן שוב, מה שהופך אותו למהיר פי 40-60 עבור מערכים גדולים.

const collator = new Intl.Collator('en', { sensitivity: 'base' });
const cities = ['berlin', 'Amsterdam', 'Zurich', 'amsterdam'];

cities.sort(collator.compare);
console.log(cities); // Output: ['Amsterdam', 'amsterdam', 'berlin', 'Zurich']

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

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

מיון אובייקטים

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

const products = [
  { name: 'Apple', price: 30 },
  { name: 'Banana', price: 20 },
  { name: 'Cherry', price: 25 }
];

// Sorting by price in ascending order
products.sort(function(a, b) {
    return a.price - b.price;
});
console.log(products);
/* Output:
[
  { name: 'Banana', price: 20 },
  { name: 'Cherry', price: 25 },
  { name: 'Apple', price: 30 }
]
*/

מיון אובייקטים לפי תכונה מסוימת הוא משימה די נפוצה בעבודה עם נתונים מובנים (Structured Data). בדוגמה מעלה המתודה sort() משתמשת בפונקציה להשוואה כדי לגשת לתכונת המחיר של כל אובייקט.

פעולה זו מבטיחה שהמערך ימוין מספרית בהתבסס על ערכי המחיר. תוכלו לשנות את ההיגיון להשוואה כדי למיין לפי תכונות אחרות או בסדר יורד.

תנו מבט בקישור המצורף למידע נוסף על עבודה עם אובייקטים ב-JavaScript.

מיון אובייקטים לפי מספר מפתחות

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

const products = [
  { name: 'Cherry', price: 25 },
  { name: 'Apple', price: 25 },
  { name: 'Banana', price: 20 }
];

products.sort(function(a, b) {
    // First, compare by price
    if (a.price !== b.price) {
        return a.price - b.price;
    }
    // If prices are equal, compare by name
    return a.name.localeCompare(b.name);
});
console.log(products);
/* Output:
[
  { name: 'Banana', price: 20 },
  { name: 'Apple', price: 25 },
  { name: 'Cherry', price: 25 }
]
*/

גישה זו עובדת בצורה אמינה מכיוון ש-sort() מובטח כיציב (Stable) החל מ-ES2019, כלומר אלמנטים עם ערכים שווים שומרים על הסדר היחסי המקורי שלהם.

מיון דינמי לפי תכונה

מה אם ברצונכם למיין לפי תכונות שונות באופן דינמי? הנה פונקציה גנרית למיון דינמי:

function dynamicSort(property) {
  return function(a, b) {
    if (a[property] < b[property]) return -1; if (a[property] > b[property]) return 1;
    return 0;
  };
}

products.sort(dynamicSort('name')); // Sorting by name
console.log(products);

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

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

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

מיון טבעי

לרשימות שכוללות מספרים בשמותיהן (למשל, שמות קבצים), מיון טבעי מבטיח סדר נכון:

const files = ['file10.txt', 'file2.txt', 'file1.txt'];

// Sorting with numeric comparison
files.sort(function(a, b) {
    return a.localeCompare(b, undefined, { numeric: true });
});
console.log(files); // Output: ['file1.txt', 'file2.txt', 'file10.txt']

מיון תאריכים

מיון מערכים של תאריכים יכול להיות פשוט באמצעות אובייקטים מסוג Date:

const dates = [new Date(2022, 5, 1), new Date(2021, 11, 25), new Date(2023, 1, 15)];

// Sorting dates in ascending order
dates.sort(function(a, b) {
    return a - b;
});
console.log(dates);
/* Output:
[
  Tue Dec 25 2021,
  Wed Jun 01 2022,
  Tue Feb 15 2023
]
*/

מיון מערכים גדולים יכול להשפיע משמעותית על הביצועים. שקלו להשתמש בספריות ממוטבות או מיון בצד השרת עבור סטים גדולים של נתונים.

מיון ללא שינוי המערך המקורי – toSorted()

החל מ-ES2023, קיימת מתודה חדשה בשם toSorted() שמאפשרת למיין מערך מבלי לשנות את המערך המקורי. בניגוד ל-sort() שמשנה את המערך במקום (in-place), המתודה toSorted() מחזירה מערך חדש וממוין.

const numbers = [10, 5, 20, 2];

const sorted = numbers.toSorted((a, b) => a - b);
console.log(sorted);  // Output: [2, 5, 10, 20]
console.log(numbers); // Output: [10, 5, 20, 2] - the original array is unchanged

מתודה זו נתמכת בכל הדפדפנים המודרניים (Chrome 110+, Firefox 115+, Safari 16+, Edge 110+). אם אתם עובדים עם קוד שצריך לשמור על אי-שינוי נתונים (Immutability), כמו למשל ב-React, המתודה toSorted() היא הבחירה המועדפת.

מספר שימושים מעשיים

מיון הוא כלי חשוב וחזק בחיים האמיתיים של כתיבת קוד. הנה מספר דוגמאות שימושיות המיישמות מיון בהקשרים שונים:

1. מיון טבלה

מיון נמצא בשימוש תכוף לארגון נתוני טבלאות באופן דינמי. הנה דוגמה פשוטה למיון טבלה לפי עמודה:

<table id="productTable">
  <tr><th>Name</th><th>Price</th></tr>
  <tr><td>Apple</td><td>30</td></tr>
  <tr><td>Banana</td><td>20</td></tr>
  <tr><td>Cherry</td><td>25</td></tr>
</table>

<button onclick="sortTable(1)">Sort by Price</button>
function sortTable(columnIndex) {
  const table = document.getElementById('productTable');
  const rows = Array.from(table.rows).slice(1); // Exclude header row
  rows.sort(function(a, b) {
    const aText = a.cells[columnIndex].innerText;
    const bText = b.cells[columnIndex].innerText;
    return parseInt(aText) - parseInt(bText);
  });

  rows.forEach(function(row) {
    table.appendChild(row); // Reattach sorted rows
  });
}

2. מיון ציוני בחינות

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

const scores = [85, 92, 78, 95, 88];

// Sorting in descending order
scores.sort(function(a, b) {
    return b - a;
});
console.log(scores); // Output: [95, 92, 88, 85, 78]

3. ארגון תאריכי אירועים

מיון תאריכי אירועים עוזר בניהול לוחות זמנים ביעילות. הנה דוגמה:

const events = [
  { name: 'Conference', date: '2024-12-01' },
  { name: 'Meeting', date: '2024-11-25' },
  { name: 'Workshop', date: '2024-12-10' }
];

// Sorting events by date in ascending order
events.sort(function(a, b) {
    return new Date(a.date) - new Date(b.date);
});
console.log(events);
/* Output:
[
  { name: 'Meeting', date: '2024-11-25' },
  { name: 'Conference', date: '2024-12-01' },
  { name: 'Workshop', date: '2024-12-10' }
]
*/

4. סינון ומיון מלאי מוצרים

במסחר האלקטרוני, מיון מוצרים לפי מחיר או דירוג הוא קריטי. הנה דוגמה פשוטה:

const products = [
  { name: 'Laptop', price: 1500 },
  { name: 'Smartphone', price: 800 },
  { name: 'Tablet', price: 600 }
];

// Sorting by price in ascending order
products.sort(function(a, b) {
    return a.price - b.price;
});
console.log(products);
/* Output:
[
  { name: 'Tablet', price: 600 },
  { name: 'Smartphone', price: 800 },
  { name: 'Laptop', price: 1500 }
]
*/

5. קביעת סדר עדיפויות למשימות

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

const tasks = [
  { task: 'Do laundry', priority: 3 },
  { task: 'Finish project', priority: 1 },
  { task: 'Grocery shopping', priority: 2 }
];

// Sorting tasks by priority in ascending order
tasks.sort(function(a, b) {
    return a.priority - b.priority;
});
console.log(tasks);
/* Output:
[
  { task: 'Finish project', priority: 1 },
  { task: 'Grocery shopping', priority: 2 },
  { task: 'Do laundry', priority: 3 }
]
*/

שאלות נפוצות

שאלות נפוצות בנושא מיון מערכים ב-JavaScript:

מדוע sort() ממיינת מספרים בצורה שגויה ללא פונקציית השוואה?
כברירת מחדל, המתודה sort() ממירה את כל האלמנטים למחרוזות ומשווה אותם לפי ערכי Unicode. לכן, המספר 10 יופיע לפני 2 כי התו "1" קודם לתו "2". כדי למיין מספרים נכון, יש להעביר פונקציית השוואה כמו (a, b) => a - b.
מה ההבדל בין sort() לבין toSorted()?
המתודה sort() משנה את המערך המקורי במקום (in-place), בעוד ש-toSorted() מחזירה מערך חדש וממוין מבלי לשנות את המקורי. המתודה toSorted() הוצגה ב-ES2023 ונתמכת בכל הדפדפנים המודרניים.
האם המיון ב-JavaScript הוא יציב (Stable)?
כן. החל מ-ES2019, המתודה sort() מובטחת כ-stable sort בכל הדפדפנים המודרניים. משמעות הדבר היא שאלמנטים שנחשבים שווים על ידי פונקציית ההשוואה ישמרו על הסדר היחסי המקורי שלהם.
איך ממיינים מערך של אובייקטים לפי מספר תכונות?
ניתן לשרשר תנאי השוואה בתוך פונקציית ההשוואה. לדוגמה, כדי למיין קודם לפי מחיר ואז לפי שם: arr.sort((a, b) => a.price - b.price || a.name.localeCompare(b.name)). התנאי הראשון ממיין לפי מחיר, ואם המחירים שווים, התנאי השני ממיין לפי שם.
מה קורה לערכי undefined בעת מיון מערך?
ערכי undefined תמיד ממוינים לסוף המערך, ופונקציית ההשוואה אינה נקראת עבורם כלל. זוהי התנהגות מובנית של JavaScript שאינה תלויה בפונקציית ההשוואה שהועברה.

סיכום

הידיעה כיצד למיין מערכים היא חיונית ואף הכרחית כשמפתחים ב-JavaScript. היא מאפשרת לכם לארגן ולשלוט בנתונים ביעילות. השליטה במתודה sort() בעבודה עם סוגי נתונים שונים, כמו גם הבנת פונקציות השוואה והכרת המתודה toSorted(), תאפשר לכם לבנות יישומים דינמיים ורספונסיביים יותר. בהצלחה!

דיון ותגובות
0 תגובות  ]
  • אנונימית 25 נובמבר 2024, 11:25

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

    פוסט מעולה!!
    נהניתי ממגוון הדוגמאות כאן. בעיקר מהמיון הדינמי. כתוב כל כך ברור.

    תודה

השאירו תגובה

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

Savvy WordPress Development official logo