Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!

⛔ Potrzebujesz wsparcia? Oceny CV? A może Code Review? ✅ Dołącz do naszej społeczności na Discordzie!

Metoda sort() w JavaScript – omówienie i przykłady użycia

Rozwiązanie, które daje szerokie możliwości

Metoda sort() ma tak szerokie zastosowanie, że zasługuje na osobny artykuł. Oto on. Dowiedz się, dlaczego domyślne sortowanie miesza liczby w „niepojęty” sposób oraz czemu litera „Ć” łamie zasady alfabetu i ustawia się za „Z”. Jak temu zaradzić? Zapraszam do omówienia działania metody sort() i przykładów kodu w JS.

Chcesz lepiej zapamiętać to zagadnienie? Wykonaj warsztat z JavaScript: Podstawy, aby osiągnąć swój cel!

Spis treści

 Do czego służy metoda sort()

Metoda sort() służy do porządkowania elementów w tablicach według określonej kolejności. Ma to duże znaczenie dla doświadczenia użytkownika (ang. user experience).

Wyobraź sobie sklep internetowy bez możliwości posortowania produktów po cenie albo chaotyczny katalog tytułów (książek, filmów) bez kolejności alfabetycznej. Dlatego właśnie metoda sort() jest tak ważna!

 Jak działa metoda sort()

 Funkcja porównująca, czyli callback

Metoda sort() jako argument przyjmuje callback, czyli funkcję, która określa, w jaki sposób posortować elementy tablicy.

Jeśli do sort() nie przekażemy argumentu, to elementy zostaną przekonwertowane na ciągi znaków, a do ich porównywania posłużą odpowiadające im reprezentacje numeryczne w UTF-16. Domyślna kolejność sortowania będzie rosnąca.

W przypadku stringów sortowanie domyślne działa (prawie) zgodnie z oczekiwaniami. Zwróć uwagę, że poniżej litery Ć i Ł ustawiły się za Z. Jak zaradzić błędom, omówimy za chwilę.

const users = ['Bochnicki', 'Ćma', 'Łęcki', 'Lebioda', 'Ziętara'];

console.log(users.sort())
// Efekt: ["Bochnicki", "Lebioda", "Ziętara", "Ćma", "Łęcki"]

W przypadku liczb jest gorzej:

const numbers = [10, 4, 27, 35];

console.log(numbers.sort())
// Efekt: [10, 27, 35, 4]

Dzieje się tak dlatego, że liczby zostały przekonwertowane na ciągi znaków, a do porównania użyto ich reprezentacji numerycznej w UTF-16 (można ją sprawdzić za pomocą metody charCodeAt()):

'10' ➡ 49 48
'4' ➡ 52
'27' ➡ 50 55
'35' ➡ 51 53

Efekt: 49 < 50 < 51 < 52
Czyli: 10, 27, 35, 4

A teraz prześledźmy mechanizm sortowania i zobaczmy, jak możemy na niego wpłynąć.

 Mechanizm sortowania

Metoda sort() porównuje elementy znajdujące się obok siebie, stosując funkcję porównującą. W zależności od wyniku tego porównania, elementy są przestawiane lub nie.

  • Jeśli wynik porównania jest mniejszy od zera, element A jest umieszczany przed elementem B.
  • Jeśli wynik jest większy od zera, element B jest umieszczany przed elementem A.
  • Jeśli wynik jest równy zero, kolejność elementów pozostaje bez zmian.

Na poniższej grafice widzisz przykład sortowania bąbelkowego liczb od najmniejszej do największej. Może być on jednym z algorytmów sortowania w JS, lecz sposób implementacji metody sort() zależy od przeglądarki. Dodatkowo w ramach optymalizacji do sortowania mogą być używane różne algorytmy (np. sortowanie szybkie czy sortowanie przez wstawianie) zależnie od rodzaju danych w tablicy czy jej długości. Efekt ich działania będzie ten sam – posortowana tablica.

Stopniowe sortowanie tablicy zgodnie z algorytmem sortowania bąbelkowego.

 Uwaga: metoda sort() mutuje tablicę!

Metoda sort() mutuje (zmienia) oryginalną tablicę, na której jest wywoływana i zwraca tę tablicę (a dokładniej referencję do niej).

const fruit = ['orange', 'apple', 'banana', 'grape'];
const fruitSorted = fruits.sort();

console.log(fruit === fruitSorted); // Efekt: true

Jeśli nie chcesz zmieniać oryginalnej tablicy, możesz utworzyć jej kopię za pomocą np. metody slice() i na tej kopii wywołać metodę sort().

const originalArray = [3, 1, 2];
const sortedArray = originalArray.slice().sort();

console.log(originalArray === sortedArray); // Efekt: false

Warto o tym pamiętać, aby przypadkowo nie stracić pierwotnych danych wskutek mutacji tablicy.

 Przykłady sortowania w JS

Dla rozgrzewki zaczniemy od liczb i liter, choć prawdopodobnie najczęściej będziesz się spotykać z tablicami obiektów – o tym za chwilę.

 Sortowanie liczb

Ponieważ zastosujemy własną funkcję porównującą, nie będziemy już „narażeni” na domyślną konwersję liczb na ciągi znaków.

Sortowanie liczb od najmniejszej do największej:

const numbers = [10, 4, 27, 8, 35];
numbers.sort((a, b) => a - b);
// Efekt: [4, 8, 10, 27, 35]

I na odwrót – sortowanie od największej do najmniejszej:

const numbers = [10, 4, 27, 8, 35];
numbers.sort((a, b) => b - a);
// Efekt: [35, 27, 10, 8, 4]

 Porządek alfabetyczny

Na początku podałam przykład sortowania stringów, w którym wszystko byłoby dobrze, gdyby polskie znaki nie wylądowały na końcu uporządkowanej tablicy. Wiemy już, że „winne” jest kodowanie UTF-16. Zobacz, jakie kody mają litery Ć, Ł i Z:

console.log("Ć".charCodeAt()) // 262
console.log("Ł".charCodeAt()) // 321
console.log("Z".charCodeAt()) // 90
// 90 < 262 < 321

Teraz widać, dlaczego sortowanie dało nam taką kolejność. Uwaga: teoretycznie sortowanie domyślne nie musi bazować na kodowaniu UTF-16 (zależy to od specyfikacji JavaScript, a jeśli ona tego nie określa, to implementacja leży po stronie twórców przeglądarek), dlatego tym bardziej warto zastosować własne rozwiązanie.

Wystarczy użyć wbudowanej w JS metody localeCompare(). Jeśli przez argument nie przekażemy do niej własnych ustawień, to skorzysta ona z ustawień językowych przeglądarki. U mnie jest to język polski, więc uzyskuję pożądany efekt:

console.log(users.sort((a, b) => a.localeCompare(b)))
// Efekt: ["Bochnicki", "Ćma", "Lebioda", "Łęcki", "Ziętara"]

 Sortowanie obiektów po wybranych wartościach

Wpisy na blogu, sklepy, archiwa – w większości przypadków będziesz pobierać i wyświetlać użytkownikowi dane z serwera. Do ich porządkowania nie zawsze potrzebujemy metody sort() (o tym na końcu artykułu), lecz prawie zawsze ma się do czynienia z tablicami obiektów. Wpis na blogu zawiera przecież np. datę utworzenia, tytuł, zawartość oraz autora, a produkt w sklepie: nazwę, cenę, opis, datę dodania itp. Najłatwiej jest umieścić takie informacje w obiekcie.

Obiekty w tablicy z łatwością posortujemy, korzystając z wartości przechowywanych pod odpowiednimi kluczami. Wygląda to bardzo podobnie jak w przypadku liczb i stringów – jedyna różnica to „dostanie się” do odpowiedniej wartości.

const products = [
  { name: 'Laptop', price: 1200 },
  { name: 'Smartphone', price: 800 },
  { name: 'Tablet', price: 500 }
];
// Argumenty a i b to obiekty, zatem możemy odwołać się do ich właściwości (tutaj jest to cena)
products.sort((a, b) => a.price - b.price);

console.log(products)
// Efekt:
/* [{
  name: "Tablet",
  price: 500
}, {
  name: "Smartphone",
  price: 800
}, {
  name: "Laptop",
  price: 1200
}] */

 Rozbudowana funkcja porównująca (callback)

Naszego callbacku nie musimy ograniczać do krótkiej instrukcji „a - b” czy „a.localeCompare(b)”, możemy wykonywać w nim więcej operacji.

Załóżmy, że mamy tablicę stringów: imion z nazwiskami. Chcemy posortować elementy po nazwisku, lecz to nie od niego rozpoczyna się każdy ze stringów! Co robić? W ciele callbacku trzeba się jakoś „dobrać” do nazwiska:

const names = ["Mateusz Żubr", "Agnieszka Kowalska-Żuk", "Anna Aniela Życz", "Miłosz Jan Bąk"];

names.sort((a, b) => {
  const lastNameA = a.split(' ').slice(-1)[0]; // Pobranie ostatniej części stringa (nazwiska)
  const lastNameB = b.split(' ').slice(-1)[0]; // Pobranie ostatniej części stringa (nazwiska)
  return lastNameA.localeCompare(lastNameB); // Porównanie nazwisk
});

console.log(names);
// Wynik: ["Miłosz Jan Bąk", "Agnieszka Kowalska-Żuk", "Mateusz Żubr", "Anna Aniela Życz"]

 Własny sposób sortowania

W sekcji Mechanizm sortowania mówiliśmy, że jeśli wynik porównania jest mniejszy od zera, to element A jest umieszczany przed elementem B. Jeśli jest większy od zera, to na odwrót itd. Możemy to wykorzystać do sortowania „na naszych warunkach”!

Za przykład posłuży nam zbiór książek. Każda książka to obiekt, w którym mamy m.in. informację o języku (właściwość language). Chcemy umożliwić użytkownikowi sortowanie książek tak, aby na górze listy wyświetlały się pozycje napisane w języku polskim. Takie sortowanie mogłoby wyglądać jak poniżej:

const books = [{
    id: 1,
    title: "Prince Caspian",
    language: "en"
  },
  {
    id: 2,
    title: "Mały książę",
    language: "pl"
  },
  {
    id: 3,
    title: "Chemia śmierci",
    language: "pl"
  },
  {
    id: 4,
    title: "Duma i uprzedzenie",
    language: "pl"
  },
    {
    id: 5,
    title: "Madame Bovary",
    language: "fre"
  }
];

books.sort((a, b) => {
  if (a.language === "pl") { // jeśli język to j. polski
    return -1 // za wynik porównania przyjmij -1 (liczbę mniejszą od zera)
  } else {
    return 1 // za wynik porównania przyjmij 1 (liczbę większą od zera)
  }
});

console.log(books);
// Efekt:
/* [{
  id: 4,
  language: "pl",
  title: "Duma i uprzedzenie"
}, {
  id: 3,
  language: "pl",
  title: "Chemia śmierci"
}, {
  id: 2,
  language: "pl",
  title: "Mały książę"
}, {
  id: 1,
  language: "en",
  title: "Prince Caspian"
}, {
  id: 5,
  language: "fre",
  title: "Madame Bovary"
}] */

 Niepotrzebne stosowanie metody sort()

Nie ma co się rozpędzać z sortowaniem, jeśli nie sprawdziliśmy… dokumentacji API! Nieraz istnieje możliwość pobrania danych już posortowanych lub przefiltrowanych za pomocą odpowiedniego URL, np.:

https://api.example.com/products?sort=price&order=asc

Zapytanie to mogłoby nam zwrócić produkty posortowane po cenie (price) rosnąco (asc od słowa ascending).

Nazwy parametrów i dostępne opcje różnią się w zależności od implementacji API, dlatego zawsze korzystaj z dokumentacji.

Możliwość takiego sortowania i filtrowania ma także JSON Server, z którego być może korzystasz już w ramach nauki asynchroniczności w JavaScripcie. Zachęcam do zapoznania się z tymi opcjami.

Metoda sort() to doskonałe narzędzie, bez którego trudno się obejść. Warto utrwalić sobie jego działanie przed nauką frameworka lub biblioteki, np. Reacta, Vue czy Angulara. Chcesz powtórzyć inne niezbędne metody? Zapraszam do artykułu „Metody tablicowe JavaScript – powtórka przed Reactem”.

Udostępnij ten artykuł:

Mentoring to efektywna nauka pod okiem doświadczonej osoby, która:

  • przekazuje Ci swoją wiedzę i nadzoruje Twoje postępy w zdobywaniu umiejętności,
  • uczy Cię dobrych praktyk i wyłapuje złe nawyki,
  • wspiera Twój rozwój i zwiększa zaangażowanie w naukę.

Mam coś dla Ciebie!

W każdy piątek rozsyłam motywujący do nauki programowania newsletter!

Dodatkowo od razu otrzymasz ode mnie e-book o wartości 39 zł. To ponad 40 stron konkretów o nauce programowania i pracy w IT.

PS Zazwyczaj wysyłam 1-2 wiadomości na tydzień. Nikomu nie będę udostępniał Twojego adresu e-mail.