Як працює метод Array sort в JavaScript

pic

Фото Faris Mohammed на Unsplash

Привіт, друзі. Сьогодні я хочу поговорити про сортування масивів у JavaScript за допомогою методу sort(). Одразу хочу зазначити, що цей метод НЕ повертає новий відсортований масив. Він сортує початковий масив і повертає посилання на нього. Це дуже важливо пам’ятати, щоб потім не зіткнутися з годинами дебагу.

Можливо, у вас логічно виникне питання, який алгоритм використовує метод sort(). І ось я вас засмучу — однозначної відповіді я не дам! Тому що кожен рушій реалізує цей метод по-своєму. Наприклад, V8 (той, що використовується в Chrome і Node.js) використовує сортування вставками (Insertion Sort) для масивів з менше ніж десяти елементами, а для більших масивів застосовує швидке сортування (Quick Sort). SpiderMonkey (Firefox) слідує їх прикладу і сортує тим самим чином. В свою чергу, JavaScriptCore (Safari) використовує різновид алгоритму сортування злиттям (Merge Sort), відомий як алгоритм TimSort. Але нам не варто сильно переживати через вибір алгоритмів рушіями: хоча вони можуть відрізнятися, на практиці це дуже рідко впливає на щоденне використання для більшості розробників.

Застосування Array sort()

Метод sort() за замовчуванням сортує елементи масиву по зростанню, попередньо перетворюючи їх на рядки та порівнюючи послідовність значень кодових одиниць UTF-16.

Звучить складно, давайте розглянемо на прикладі:

const students = ["Emma", "Tom", "Anna", "Lucia", "Sofia"];  
students.sort();  
console.log(students); // Вивід: ["Anna", "Emma", "Lucia", "Sofia", "Tom"]

З рядками все сортується відмінно. В результаті ми отримуємо масив з елементами, відсортованими за зростанням.

Але що з числами? Якщо ми будемо сортувати їх так само, отримаємо наступний результат:

const numbers = [2, 10, 4, 58, 1, 3, 9];  
numbers.sort();  
console.log(numbers); // Вивід: [1, 10, 2, 3, 4, 58, 9]

Ми можемо помітити, що елементи відсортовані не так, як ми очікували. Це пов’язано з тим, що числа перетворюються на рядки, і сортування виконується посимвольно, на основі їх значень у таблиці кодів UTF-16 (аналогічно таблиці ASCII для латинських символів). Наприклад, рядок “10” починається з символу “1”, який йде раніше за “2”, тому “10” знаходиться одразу після “1”. Але як уникнути такої поведінки? В таких випадках ми можемо передати колбек в метод sort():

const numbers = [2, 10, 4, 58, 1, 3, 9];  
numbers.sort((a, b) => a - b);  
console.log(numbers); // Вивід: [1, 2, 3, 4, 9, 10, 58]

Тепер масив сортується правильно, але давайте розглянемо функцію, яку ми передаємо в метод sort() більш детально.

Функція порівняння приймає два аргументи — два елементи масиву, які потрібно порівняти. Вона повинна повернути:

  • Від'ємне число, якщо перший елемент повинен бути розташований перед другим.
  • Нуль, якщо порядок цих двох елементів не важливий.
  • Позитивне число, якщо перший елемент повинен бути розташований після другого.

Повернуте значення обов’язково повинно бути числом, а не булевим значенням, наприклад. Інакше ви не досягнете бажаного результату.

Щоб відсортувати масив за спаданням, потрібно просто змінити порядок порівняння в функції. Замість віднімання a - b (яке сортує за зростанням), використовуємо b - a.
Это указывает, что элементы с большими значениями должны идти раньше элементов с меньшими значениями.

const numbers = [2, 10, 4, 58, 1, 3, 9];  
numbers.sort((a, b) => b - a);  
console.log(numbers); // Вивід: [58, 10, 9, 4, 3, 2, 1]

Давайте також розглянемо, як сортувати інші типи даних:

Дати

На щастя, в JavaScript дати представлені як числові значення (таймстемпи) — кількість мілісекунд з 1 січня 1970 року, а отже й порівнювати ми можемо їх як числа:

const dates = [  
 new Date('2023-12-31'),  
 new Date('2023-01-01'),  
 new Date('2023-06-15')  
];  
dates.sort((a, b) => a - b);  
console.log(dates); // Вивід: [  
// 2023-01-01T00:00:00.000Z,  
// 2023-06-15T00:00:00.000Z,  
// 2023-12-31T00:00:00.000Z  
// ]

Майте на увазі, якщо для роботи з датами ви використовуєте бібліотеки (такі як moment або date-fns), використовуйте методи з цих бібліотек для отримання таймстемпів та порівнюйте їх.

Об'єкти

Ви також можете сортувати об'єкти за їх властивостями, будь то рядок, число чи дата:

const events = [  
 { name: 'Event 1', date: new Date('2023-06-15') },  
 { name: 'Event 2', date: new Date('2023-01-01') },  
 { name: 'Event 3', date: new Date('2023-12-31') }  
];  

events.sort((a, b) => a.date - b.date);  

console.log(events); // Вивід: [  
// { name: 'Event 2', date: new Date('2023-01-01') },  
// { name: 'Event 1', date: new Date('2023-06-15') },  
// { name: 'Event 3', date: new Date('2023-12-31') }  
// ]

Або навіть сортувати за кількома властивостями:

const events = [  
 { name: 'Event 1', date: new Date('2023-06-15'), price: 123 },  
 { name: 'Event 2', date: new Date('2023-01-01'), price: 256 },  
 { name: 'Event 3', date: new Date('2023-12-31'), price: 868 },  
 { name: 'Event 4', date: new Date('2023-12-31'), price: 435 },  
];  

events.sort((a, b) => {  
 const dateA = a.date.getTime();  
 const dateB = b.date.getTime();  

 if (dateA !== dateB) {  
 return dateA - dateB; // Сортування за датою  
 }  

 return a.price - b.price; // Якщо дата однакова, сортування за ціною  
});  

console.log(events); // Вивід: [  
// { name: 'Event 2', date: new Date('2023-01-01'), price: 256 },  
// { name: 'Event 1', date: new Date('2023-06-15'), price: 123 },  
// { name: 'Event 4', date: new Date('2023-12-31'), price: 435 },  
// { name: 'Event 3', date: new Date('2023-12-31'), price: 868 }  
// ]

Сортування може працювати некоректно, якщо порівнюване властивість відсутнє в об'єкті. Якщо ви не впевнені, чи знаходиться властивість, за яким ви порівнюєте об'єкти, у всіх об'єктах, то краще перестрахуватися і оголосити в колбеку змінні з дефолтними значеннями перед порівнянням:

const events = [  
 { name: 'Event 1', date: new Date('2023-06-15') },  
 { name: 'Event 2', date: new Date('2023-01-01') },  
 { name: 'Event 3'}  
];  

events.sort((a, b) => {  
 const dateA = a.date ? a.date.getTime() : 0;  
 const dateB = b.date ? b.date.getTime() : 0;  

 return dateA - dateB;   
});  

console.log(events); // Вивід: [  
// { name: 'Event 3' }  
// { name: 'Event 2', date: new Date('2023-01-01') },  
// { name: 'Event 1', date: new Date('2023-06-15') },  
// ]

Звісно, значення за замовчуванням вибирайте з урахуванням логіки вашої програми.

Висновки

Звісно, метод sort() значно полегшить вам життя, коли ви будете справлятися з повсякденними завданнями, але не варто забувати, що інколи він може бути підступним.

Не втомлюся повторювати, що він змінює початковий масив, тому якщо вам потрібно зберегти його у первісному вигляді, краще сортувати його копію.

Також потрібно бути уважними при сортуванні чисел та інших типів даних, наприклад, дат та об'єктів. Пам’ятаємо, що якщо у нас тип даних не рядок, потрібно передавати функцію порівняння і враховувати відсутність властивостей, за якими ми сортуємо, якщо працюємо з масивом об'єктів.

Сподіваюся, що з цією інформацією ви не будете проводити безсонні ночі, намагаючись зрозуміти, де ж у вас може бути помилка.
А сортування масивів стане для вас приємною рутинною справою, а не непередбачуваною боротьбою. Дякую за увагу та удачі в програмуванні!🚀🚀🚀

Перекладено з: Как работает Array sort метод в JavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *