Przetwarzanie tablic — od danych do rankingu
Wybierz aktywnych graczy, posortuj punkty i zbuduj raport. Naucz się dobierać metodę do pytania, zamiast każdą operację wciskać w forEach.
Po tej lekcji potrafisz
- Dobierzesz map, filter, find, some, every i reduce.
- Posortujesz liczby oraz obiekty z poprawnym komparatorem.
- Rozpoznasz metody zmieniające tablicę i płytkie kopie.
Kto awansował do finału?
Masz wyniki wszystkich osób, ale raport ma pokazać tylko aktywnych finalistów, od najlepszego. Podziel pracę na kroki: wybór, uporządkowanie, opis. Każdy krok będzie miał czytelny wynik.
Małe kroki, konkretne efekty
Wybór metody zależy od wyniku
map tworzy nową wartość dla każdego elementu, filter wybiera elementy, find zwraca pierwszy pasujący lub undefined. some pyta „czy którykolwiek?”, every „czy wszystkie?”.
const punkty = [20, 50, 80];
console.log(punkty.map(p => p + 5).join(","));
console.log(punkty.filter(p => p >= 50).join(","));
console.log(punkty.find(p => p >= 50));
console.log(punkty.some(p => p === 80), punkty.every(p => p >= 50));
25,55,85
50,80
50
true false
Dla pustej tablicy some daje false, every true. Nie ma elementu łamiącego warunek. Jeśli potrzebujesz przynajmniej jednej osoby, dodaj sprawdzenie length > 0.
reduce zbiera wynik, sort zmienia kolejność
reduce przekazuje akumulator i kolejny element do callbacku. Wartość początkowa obsługuje także pustą tablicę. sort bez komparatora porównuje tekstowe reprezentacje, więc do liczb podaj (a, b) => a – b.
const liczby = [10, 2, 30];
const suma = liczby.reduce((razem, n) => razem + n, 0);
const rosnaco = [...liczby].sort((a, b) => a - b);
console.log(suma, rosnaco.join(","));
console.log(liczby.join(","));
42 2,10,30
10,2,30
Ujemny wynik komparatora oznacza, że a powinno być przed b; dodatni — odwrotnie, a zero równorzędność sortowania. Spread zabezpiecza kolejność oryginalnej tablicy.
Ranking finalistów
Remis punktowy rozstrzygamy alfabetycznie po nicku. Dla danych ćwiczeniowych używamy nicków bez polskich znaków.
interface Gracz { nick: string; punkty: number; aktywny: boolean }
const gracze: Gracz[] = [
{ nick: "Neo", punkty: 70, aktywny: true },
{ nick: "Ala", punkty: 90, aktywny: true },
{ nick: "Max", punkty: 99, aktywny: false },
{ nick: "Ola", punkty: 70, aktywny: true }
];
const finalisci = gracze
.filter(g => g.aktywny && g.punkty >= 50)
.sort((a, b) => b.punkty - a.punkty || a.nick.localeCompare(b.nick, "pl"));
finalisci.forEach((g, i) => console.log((i + 1) + ". " + g.nick + " " + g.punkty));
console.log("Suma: " + finalisci.reduce((s, g) => s + g.punkty, 0));
1. Ala 90
2. Neo 70
3. Ola 70
Suma: 230
Jak program dochodzi do wyniku
- filter tworzy nową tablicę, więc następujący po nim sort nie zmienia kolejności gracze.
- b.punkty – a.punkty daje porządek malejący.
- Gdy różnica wynosi zero, || uruchamia porównanie nicków. To celowy wybór przy remisie.
- Obiekty graczy pozostają wspólne między tablicami. Nie zmieniamy ich pól w callbackach.
Zanim przejdziesz dalej
Czy finalisci[0].punkty = 0 mogłoby zmienić obiekt w gracze?
Sprawdź swoje rozumowanie
Tak. Nowa tablica nie oznacza nowych obiektów. filter kopiuje referencje do wybranych elementów.
Zmień kod i zobacz, co się stanie
flatMap łączy mapowanie i spłaszczenie
Gdy jeden element ma dać kilka wyników, flatMap spłaszcza wynik o jeden poziom. Z kolei findIndex daje indeks lub -1, co trzeba sprawdzić przed usuwaniem.
const grupy = [["Ala", "Neo"], ["Ola"]];
console.log(grupy.flatMap(grupa => grupa).join(", "));
const lista = ["Ala", "Neo"];
const indeks = lista.findIndex(nick => nick === "Max");
if (indeks !== -1) { lista.splice(indeks, 1); }
console.log(lista.join(", "));
Ala, Neo, Ola
Ala, Neo
splice(-1, 1) usunęłoby ostatni element — mimo braku szukanego nicku. W nowszym JavaScripcie są też toSorted, toReversed i toSpliced, które zwracają nowe tablice; wymagają odpowiedniego środowiska i biblioteki typów ES2023. W naszym ustawieniu ES2022 stosujemy kopię i poznane metody.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| reduce bez wartości początkowej na [] | Spowoduje wyjątek. Przy sumie podaj 0. |
| sort liczb bez komparatora | 10 może znaleźć się przed 2, bo porównanie jest tekstowe. |
| findIndex użyte bez kontroli -1 | Może doprowadzić do usunięcia innego elementu. |
JSON.stringify tylko do wypisania
W jednym z rozwiązań używamy JSON.stringify, żeby wygodnie wypisać obiekt w konsoli. To
jedyne zastosowanie w tej lekcji. Zamianę danych na tekst i z powrotem, razem z walidacją, omawia lekcja 29.
Teraz Twoja kolej
Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.
Do każdej ceny [10, 20] dodaj 5 przez map. Oryginał ma pozostać bez zmian.
Mała podpowiedź
Zwróć nową liczbę.
Przykładowe rozwiązanie z wyjaśnieniem
const ceny = [10, 20];
console.log(ceny.map(c => c + 5).join(","));
console.log(ceny.join(","));
15,25
10,20
map tworzy nową tablicę wyników.
Funkcja gotowa ma zwracać true tylko dla niepustej listy samych true. Sprawdź [], [true, true], [true, false].
Mała podpowiedź
Połącz length i every.
Przykładowe rozwiązanie z wyjaśnieniem
function gotowa(gracze: boolean[]): boolean { return gracze.length > 0 && gracze.every(g => g); }
console.log(gotowa([]), gotowa([true, true]), gotowa([true, false]));
false true false
Samo every([]) dawałoby true, co nie odpowiada regule zadania.
Z [30, 100, 20, 80] wybierz trzy największe wyniki malejąco. Nie zmieniaj wejścia.
Mała podpowiedź
Kopia, sort, slice(0, 3).
Przykładowe rozwiązanie z wyjaśnieniem
const wyniki = [30, 100, 20, 80];
const podium = [...wyniki].sort((a, b) => b - a).slice(0, 3);
console.log(podium.join(","));
console.log(wyniki.join(","));
100,80,30
30,100,20,80
slice nie obejmuje indeksu końcowego i działa też dla krótszej listy.
Produkty mają cena i sztuki. Oblicz łączną liczbę sztuk i koszt dla (10, 2) oraz (5, 3), używając reduce. Sprawdź pusty koszyk.
Mała podpowiedź
Akumulator może być obiektem z dwoma polami.
Przykładowe rozwiązanie z wyjaśnieniem
type Pozycja = { cena: number; sztuki: number };
function raport(koszyk: Pozycja[]) {
return koszyk.reduce((s, p) => ({ sztuki: s.sztuki + p.sztuki, koszt: s.koszt + p.cena * p.sztuki }), { sztuki: 0, koszt: 0 });
}
console.log(JSON.stringify(raport([{ cena: 10, sztuki: 2 }, { cena: 5, sztuki: 3 }])));
console.log(JSON.stringify(raport([])));
{"sztuki":5,"koszt":35}
{"sztuki":0,"koszt":0}
JSON.stringify pokazuje pola wyniku jako tekst. Szczegóły JSON-u poznasz w lekcji 29.
Co zabierasz do następnej lekcji
- Wybierz metodę po rodzaju potrzebnego wyniku.
- Pilnuj pustych list i braku dopasowania.
- Nowa tablica może nadal zawierać te same obiekty.