Kurs JavaScript i TypeScript · Moduł 4: Programowanie asynchroniczne

Przetwarzanie danych JSON i zarządzanie złożonymi strukturami danych

9 min czytania
W tej lekcji4

W poprzednich ćwiczeniach nauczyliśmy się pobierać dane z serwerów API za pomocą Fetch i tworzyć zaawansowane klienty HTTP dla naszego Parku Jurajskiego. Teraz przyszedł czas, aby nauczyć się efektywnie przetwarzać, transformować i zarządzać złożonymi strukturami danych JSON, które otrzymujemy z naszych systemów.

JSON (JavaScript Object Notation) to standardowy format wymiany danych, który jest lekki, łatwy do odczytania przez ludzi i prosty do przetwarzania przez maszyny. W Parku Jurajskim używamy JSON do reprezentowania wszystkiego - od danych dinozaurów po statusy systemów bezpieczeństwa i dane odwiedzających.

Podstawy JSON w JavaScript

JSON w JavaScript jest reprezentowany przez dwa główne typy struktur:

  • Obiekty (obiekty JavaScript / hashmapy)
  • Tablice (listy elementów)

Przykładowa struktura JSON reprezentująca dinozaura w naszym parku:

1const trex = {
2  "id": "dino-001",
3  "nazwa": "Rexy",
4  "gatunek": "Tyrannosaurus Rex",
5  "wiek": 25,
6  "statystyki": {
7    "waga": 8000,
8    "dlugosc": 12.3,
9    "wysokosc": 3.6
10  },
11  "diety": ["mięso", "padlina"],
12  "zagrożenie": 5,
13  "lokalizacja": {
14    "wybieg": "T-Rex Kingdom",
15    "koordynaty": {
16      "x": 34.5,
17      "y": 67.2
18    }
19  },
20  "zachowania": [
21    {
22      "typ": "polowanie",
23      "czestosc": "wysoka",
24      "pory": ["rano", "wieczór"]
25    },
26    {
27      "typ": "odpoczynek",
28      "czestosc": "średnia",
29      "pory": ["południe"]
30    }
31  ],
32  "dataDodania": "2023-06-15T08:30:00Z",
33  "aktywny": true
34}

Parsowanie i serializacja JSON

JavaScript oferuje dwie podstawowe metody do pracy z JSON:

  1. JSON.parse() - konwertuje string JSON na obiekt JavaScript
  2. JSON.stringify() - konwertuje obiekt JavaScript na string JSON
1// Serializacja obiektu do stringa JSON
2const trexString = JSON.stringify(trex);
3console.log("Zserializowany T-Rex:", trexString);
4
5// Parsowanie stringa JSON z powrotem do obiektu
6const parsedTrex = JSON.parse(trexString);
7console.log("Sparsowany T-Rex:", parsedTrex);

Dodatkowe parametry JSON.stringify() i JSON.parse()

JSON.stringify() ma dodatkowe parametry, które mogą być przydatne:

1// Formatowanie JSON z wcięciami dla czytelności
2const prettyTrex = JSON.stringify(trex, null, 2);
3console.log("Sformatowany T-Rex:");
4console.log(prettyTrex);
5
6// Selektywne serializowanie tylko wybranych pól
7const simpleTrex = JSON.stringify(trex, ['id', 'nazwa', 'gatunek', 'wiek']);
8console.log("Uproszczony T-Rex:", simpleTrex);
9
10// Własna funkcja zastępcza (replacer)
11const customTrex = JSON.stringify(trex, (key, value) => {
12  // Ukrywamy dokładne współrzędne z powodów bezpieczeństwa
13  if (key === 'koordynaty') return "UKRYTE";
14  // Kody poziomu zagrożenia konwertujemy na opis tekstowy
15  if (key === 'zagrożenie') {
16    const opisy = ["niegroźny", "niski", "umiarkowany", "wysoki", "ekstremalny"];
17    return opisy[value - 1] || value;
18  }
19  return value;
20});
21console.log("T-Rex z niestandardową serializacją:", customTrex);

Techniki przetwarzania złożonych struktur JSON

Załóżmy, że otrzymaliśmy złożoną strukturę JSON zawierającą dane o wszystkich dinozaurach w parku. Jak możemy efektywnie przetwarzać takie dane?

1. Filtrowanie danych

Filtrowanie to jedna z najczęstszych operacji podczas pracy z kolekcjami danych:

1// Załóżmy, że mamy tablicę dinozaurów
2const dinozaury = await api.get('/dinozaury');
3
4// Filtrowanie tylko drapieżników
5const drapiezniki = dinozaury.filter(dino => dino.diety.includes('mięso'));
6
7// Filtrowanie dinozaurów w określonym wybiegu
8const dinoWWybiegu = dinozaury.filter(dino =>
9  dino.lokalizacja.wybieg === 'T-Rex Kingdom'
10);
11
12// Filtrowanie niebezpiecznych dinozaurów (poziom zagrożenia > 3)
13const niebezpieczneDino = dinozaury.filter(dino => dino.zagrożenie > 3);
14
15// Złożone filtrowanie z wieloma warunkami
16const niebezpieczneDrapieznikiWZonie = dinozaury.filter(dino =>
17  dino.zagrożenie > 3 &&
18  dino.diety.includes('mięso') &&
19  dino.lokalizacja.wybieg === 'Zone A'
20);

2. Mapowanie i transformacja danych

Często potrzebujemy przekształcić dane z jednego formatu na inny, np. do wyświetlenia na interfejsie użytkownika:

1// Tworzymy uproszczoną listę dinozaurów dla UI
2const prosteDino = dinozaury.map(dino => ({
3  id: dino.id,
4  nazwa: dino.nazwa,
5  gatunek: dino.gatunek,
6  zagrożenie: dino.zagrożenie
7}));
8
9// Dodajemy nowe pola na podstawie istniejących danych
10const dinoZDodatkowymiInformacjami = dinozaury.map(dino => ({
11  ...dino, // kopiujemy wszystkie istniejące pola
12  wiekLat: dino.wiek, // przemianowanie pola
13  zagrozenieOpis: ['niegroźny', 'niski', 'umiarkowany', 'wysoki', 'ekstremalny'][dino.zagrożenie - 1],
14  waga_tony: dino.statystyki.waga / 1000,
15  wymaga_ochrony: dino.zagrożenie > 3
16}));
17
18// Transformacja do specyficznego formatu dla wykresu
19const daneDoWykresu = dinozaury.map(dino => ({
20  x: dino.statystyki.dlugosc,
21  y: dino.statystyki.waga,
22  r: dino.zagrożenie * 5, // rozmiar punktu zależny od zagrożenia
23  label: dino.nazwa,
24  color: dino.diety.includes('mięso') ? 'red' : 'green'
25}));

3. Agregacja i redukcja danych

Redukcja pozwala na agregowanie danych w celu uzyskania podsumowań i statystyk:

1// Obliczenie średniej wagi wszystkich dinozaurów
2const sredniWaga = dinozaury.reduce((suma, dino) => suma + dino.statystyki.waga, 0) / dinozaury.length;
3
4// Znalezienie najdłuższego dinozaura
5const najdluzszy = dinozaury.reduce((najdluzszy, dino) =>
6  dino.statystyki.dlugosc > najdluzszy.statystyki.dlugosc ? dino : najdluzszy
7, dinozaury[0]);
8
9// Grupowanie dinozaurów według gatunków
10const dinoPoGatunkach = dinozaury.reduce((grupy, dino) => {
11  // Jeśli grupa dla tego gatunku nie istnieje, tworzymy ją
12  if (!grupy[dino.gatunek]) {
13    grupy[dino.gatunek] = [];
14  }
15  // Dodajemy dinozaura do odpowiedniej grupy
16  grupy[dino.gatunek].push(dino);
17  return grupy;
18}, {});
19
20// Liczenie dinozaurów według poziomu zagrożenia
21const liczbaPoZagrozeniu = dinozaury.reduce((licznik, dino) => {
22  licznik[dino.zagrożenie] = (licznik[dino.zagrożenie] || 0) + 1;
23  return licznik;
24}, {});

4. Łączenie operacji z użyciem łańcuchów metod

Metody tablicowe można łączyć w łańcuchy, co pozwala na pisanie zwięzłego i czytelnego kodu:

1// Znajdź 3 najniebezpieczniejsze dinozaury w Zone A
2const top3NiebezpieczneWZoneA = dinozaury
3  .filter(dino => dino.lokalizacja.wybieg === 'Zone A')
4  .sort((a, b) => b.zagrożenie - a.zagrożenie) // sortowanie malejąco
5  .slice(0, 3) // weź tylko pierwsze 3
6  .map(dino => ({
7    nazwa: dino.nazwa,
8    gatunek: dino.gatunek,
9    zagrożenie: dino.zagrożenie
10  }));
11
12// Oblicz średnią wagę dla każdego gatunku
13const sredniaWagaPoGatunkach = Object.entries(
14  dinozaury.reduce((grupy, dino) => {
15    if (!grupy[dino.gatunek]) {
16      grupy[dino.gatunek] = { suma: 0, liczba: 0 };
17    }
18    grupy[dino.gatunek].suma += dino.statystyki.waga;
19    grupy[dino.gatunek].liczba += 1;
20    return grupy;
21  }, {})
22).map(([gatunek, { suma, liczba }]) => ({
23  gatunek,
24  sredniaWaga: suma / liczba
25}));

5. Głębokie znajdowanie i aktualizowanie danych

W złożonych strukturach często musimy znaleźć lub zaktualizować głęboko zagnieżdżone dane. Oto kilka podejść:

1// Znajdowanie dinozaura na podstawie głęboko zagnieżdżonego warunku
2const dinoWPobliżuKoordynatów = dinozaury.find(dino => {
3  const { x, y } = dino.lokalizacja.koordynaty;
4  // Szukamy dinozaura w promieniu 5 jednostek od punktu (30, 70)
5  return Math.sqrt(Math.pow(x - 30, 2) + Math.pow(y - 70, 2)) < 5;
6});
7
8// Aktualizacja głęboko zagnieżdżonej właściwości (immutability)
9function updateDinoLocation(dino, newX, newY) {
10  return {
11    ...dino,
12    lokalizacja: {
13      ...dino.lokalizacja,
14      koordynaty: {
15        ...dino.lokalizacja.koordynaty,
16        x: newX,
17        y: newY
18      }
19    }
20  };
21}
22
23// Użycie
24const updatedDino = updateDinoLocation(trex, 35.0, 68.0);

6. Rekurencyjne przetwarzanie danych

Niektóre struktury danych są hierarchiczne lub mają nieznany poziom zagnieżdżenia. W takich przypadkach przydaje się przetwarzanie rekurencyjne:

1// Przykładowa hierarchiczna struktura kategorii dinozaurów
2const kategorieDinozaurów = {
3  "nazwa": "Dinozaury",
4  "podkategorie": [
5    {
6      "nazwa": "Teropody",
7      "podkategorie": [
8        {
9          "nazwa": "Tyranozauroidy",
10          "dinozaury": [
11            { "id": "dino-001", "nazwa": "Rexy" },
12            { "id": "dino-002", "nazwa": "Tara" }
13          ]
14        },
15        {
16          "nazwa": "Deinonychoidy",
17          "dinozaury": [
18            { "id": "dino-003", "nazwa": "Blue" },
19            { "id": "dino-004", "nazwa": "Delta" }
20          ]
21        }
22      ]
23    },
24    {
25      "nazwa": "Zauropody",
26      "dinozaury": [
27        { "id": "dino-005", "nazwa": "Długi" },
28        { "id": "dino-006", "nazwa": "Wysoki" }
29      ]
30    }
31  ]
32};
33
34// Funkcja rekurencyjna do znajdowania dinozaura według ID w hierarchii
35function znajdzDinozauraPoId(wezel, id) {
36  // Sprawdź, czy obecny węzeł ma tablicę dinozaurów
37  if (wezel.dinozaury) {
38    const znaleziony = wezel.dinozaury.find(dino => dino.id === id);
39    if (znaleziony) return znaleziony;
40  }
41
42  // Jeśli węzeł ma podkategorie, przeszukaj je rekurencyjnie
43  if (wezel.podkategorie) {
44    for (const podkategoria of wezel.podkategorie) {
45      const znaleziony = znajdzDinozauraPoId(podkategoria, id);
46      if (znaleziony) return znaleziony;
47    }
48  }
49
50  // Nic nie znaleziono
51  return null;
52}
53
54// Funkcja rekurencyjna do spłaszczania hierarchii i tworzenia listy wszystkich dinozaurów
55function pobierzWszystkieDinozaury(wezel, lista = []) {
56  if (wezel.dinozaury) {
57    lista.push(...wezel.dinozaury);
58  }
59
60  if (wezel.podkategorie) {
61    for (const podkategoria of wezel.podkategorie) {
62      pobierzWszystkieDinozaury(podkategoria, lista);
63    }
64  }
65
66  return lista;
67}
68
69// Użycie
70const blue = znajdzDinozauraPoId(kategorieDinozaurów, "dino-003");
71console.log("Znaleziony dinozaur:", blue);
72
73const wszystkieDino = pobierzWszystkieDinozaury(kategorieDinozaurów);
74console.log("Wszystkie dinozaury:", wszystkieDino);

Praktyczny przykład: System monitorowania dinozaurów

Teraz połączmy wszystkie omówione techniki w jednym praktycznym przykładzie. Załóżmy, że tworzymy panel kontrolny dla kierownika Parku Jurajskiego, który pokazuje status wszystkich dinozaurów w parku.

1// Funkcja pobierająca dane z API
2async function pobierzDaneDinozaurów() {
3  try {
4    const odpowiedź = await fetch('https://api.jurassic-park.com/dinozaury');
5    if (!odpowiedź.ok) throw new Error(`Błąd HTTP: ${odpowiedź.status}`);
6    return await odpowiedź.json();
7  } catch (błąd) {
8    console.error('Błąd pobierania danych:', błąd);
9    // W przypadku błędu zwracamy puste dane
10    return { dinozaury: [] };
11  }
12}
13
14// Funkcja do przetwarzania i przygotowania danych do wyświetlenia
15function przetwórzDaneDoWyświetlenia(dane) {
16  // Pobieranie głównej tablicy dinozaurów
17  const { dinozaury } = dane;
18
19  // Statystyki zbiorcze
20  const statystyki = {
21    liczba: dinozaury.length,
22    liczbaGatunków: new Set(dinozaury.map(d => d.gatunek)).size,
23    średniaWaga: dinozaury.reduce((suma, d) => suma + d.statystyki.waga, 0) / dinozaury.length,
24    maksymalneZagrożenie: Math.max(...dinozaury.map(d => d.zagrożenie))
25  };
26
27  // Grupowanie według wybiegów
28  const dinozauryPoWybiegach = dinozaury.reduce((grupy, dino) => {
29    const wybieg = dino.lokalizacja.wybieg;
30    if (!grupy[wybieg]) grupy[wybieg] = [];
31    grupy[wybieg].push(dino);
32    return grupy;
33  }, {});
34
35  // Liczenie dinozaurów według poziomów zagrożenia
36  const liczbaPoZagrożeniu = [1, 2, 3, 4, 5].map(poziom => ({
37    poziom,
38    liczba: dinozaury.filter(d => d.zagrożenie === poziom).length,
39    procent: (dinozaury.filter(d => d.zagrożenie === poziom).length / dinozaury.length) * 100
40  }));
41
42  // Identyfikacja potencjalnych zagrożeń
43  const zagrożenia = dinozaury
44    .filter(d => d.zagrożenie >= 4)
45    .map(d => ({
46      id: d.id,
47      nazwa: d.nazwa,
48      gatunek: d.gatunek,
49      wybieg: d.lokalizacja.wybieg,
50      zagrożenie: d.zagrożenie,
51      ostatnieZachowanie: d.zachowania[0]?.typ || 'nieznane',
52      koordynaty: d.lokalizacja.koordynaty
53    }))
54    .sort((a, b) => b.zagrożenie - a.zagrożenie);
55
56  // Przygotowanie danych do wykresu wagowego
57  const daneDoWykresuWagi = dinozaury
58    .filter(d => d.statystyki.waga > 0) // Odfiltruj brakujące dane
59    .map(d => ({
60      id: d.id,
61      nazwa: d.nazwa,
62      gatunek: d.gatunek,
63      waga: d.statystyki.waga,
64      drapieżnik: d.diety.includes('mięso'),
65      rozmiarPunktu: Math.sqrt(d.statystyki.waga) / 5
66    }));
67
68  // Ostatnie aktywności (sortowane według daty)
69  const ostatnieAktywności = dinozaury
70    .filter(d => d.zachowania && d.zachowania.length > 0)
71    .flatMap(d => d.zachowania.map(z => ({
72      dinoId: d.id,
73      dinoNazwa: d.nazwa,
74      typ: z.typ,
75      czestosc: z.czestosc,
76      pory: z.pory
77    })))
78    .sort((a, b) => a.dinoId.localeCompare(b.dinoId));
79
80  // Zwróć przetworzone dane
81  return {
82    statystyki,
83    dinozauryPoWybiegach,
84    liczbaPoZagrożeniu,
85    zagrożenia,
86    daneDoWykresuWagi,
87    ostatnieAktywności,
88    surowe: dinozaury // Oryginalne dane, gdyby były potrzebne
89  };
90}
91
92// Funkcja do renderowania danych na interfejsie użytkownika (przykład)
93function wyrenderujPanelKontrolny(przetworzoneDane) {
94  // Tutaj kod aktualizujący widoki UI, wykresy, tabele, mapy, itp.
95  console.log("Przetworzono dane do panelu kontrolnego:", przetworzoneDane);
96
97  // Przykład renderowania tabeli z zagrożeniami
98  const tabelaZagrożeń = document.getElementById('tabela-zagrozen');
99  if (tabelaZagrożeń) {
100    tabelaZagrożeń.innerHTML = przetworzoneDane.zagrożenia
101      .map(z => `
102        <tr>
103          <td>${z.nazwa}</td>
104          <td>${z.gatunek}</td>
105          <td>${z.wybieg}</td>
106          <td class="zagrozenie-${z.zagrożenie}">${z.zagrożenie}</td>
107          <td>${z.ostatnieZachowanie}</td>
108          <td>
109            <button onclick="pokazNaMapie(${z.koordynaty.x}, ${z.koordynaty.y})">
110              Pokaż na mapie
111            </button>
112          </td>
113        </tr>
114      `)
115      .join('');
116  }
117
118  // ... pozostałe elementy UI
119}
120
121// Główna funkcja aktualizacji danych
122async function aktualizujPanel() {
123  // Pobranie danych z API
124  const dane = await pobierzDaneDinozaurów();
125
126  // Przetworzenie danych
127  const przetworzoneDane = przetwórzDaneDoWyświetlenia(dane);
128
129  // Wyrenderowanie panelu
130  wyrenderujPanelKontrolny(przetworzoneDane);
131}
132
133// Uruchomienie aktualizacji co 5 minut
134aktualizujPanel();
135setInterval(aktualizujPanel, 5 * 60 * 1000);

Podsumowanie

W tym ćwiczeniu nauczyliśmy się:

  1. Podstaw serializacji i deserializacji JSON - jak konwertować dane między formatem tekstowym a obiektami JavaScript

  2. Zaawansowanych technik przetwarzania danych:

    • Filtrowanie danych za pomocą .filter()
    • Transformacja danych za pomocą .map()
    • Agregacja danych za pomocą .reduce()
    • Łączenie operacji w łańcuchy
    • Praca z głęboko zagnieżdżonymi strukturami
    • Rekurencyjne przetwarzanie hierarchicznych danych
  3. Praktycznego zastosowania tych technik w kontekście systemu zarządzania parkiem pełnym dinozaurów

Umiejętność efektywnego przetwarzania i analizy danych JSON jest kluczowa dla każdego współczesnego projektu JavaScript - od prostych stron internetowych po złożone aplikacje jak system zarządzania Parkiem Jurajskim. Dzięki technikom, które poznałeś, będziesz mógł przekształcać surowe dane w użyteczne informacje, które pomogą utrzymać park funkcjonujący sprawnie i bezpiecznie... przynajmniej do czasu, gdy raptor znajdzie lukę w systemie!

W następnym ćwiczeniu przyjrzymy się jak tworzyć i korzystać z Web Workers, aby przenieść intensywne obliczenia (jak analizy zachowań dinozaurów) do oddzielnych wątków, nie blokując głównego interfejsu użytkownika.

Kod do tej lekcji: index.js
1// Przetwarzanie danych JSON - Park Jurajski
2// Praktyka: filtrowanie, mapowanie, redukcja, rekurencja
3
4// === Dane dinozaurow w parku ===
5const dinozaury = [
6  {
7    id: "dino-001",
8    nazwa: "Rexy",
9    gatunek: "Tyrannosaurus Rex",
10    wiek: 25,
11    statystyki: { waga: 8000, dlugosc: 12.3, wysokosc: 3.6 },
12    diety: ["mieso", "padlina"],
13    zagrozenie: 5,
14    lokalizacja: { wybieg: "T-Rex Kingdom", koordynaty: { x: 34.5, y: 67.2 } },
15    zachowania: [
16      { typ: "polowanie", czestosc: "wysoka", pory: ["rano", "wieczor"] }
17    ],
18    aktywny: true
19  },
20  {
21    id: "dino-002",
22    nazwa: "Blue",
23    gatunek: "Velociraptor",
24    wiek: 8,
25    statystyki: { waga: 150, dlugosc: 3.0, wysokosc: 1.8 },
26    diety: ["mieso"],
27    zagrozenie: 4,
28    lokalizacja: { wybieg: "Raptor Paddock", koordynaty: { x: 50.1, y: 30.4 } },
29    zachowania: [
30      { typ: "polowanie", czestosc: "wysoka", pory: ["rano", "noc"] },
31      { typ: "trening", czestosc: "srednia", pory: ["poludnie"] }
32    ],
33    aktywny: true
34  },
35  {
36    id: "dino-003",
37    nazwa: "Brachio",
38    gatunek: "Brachiosaurus",
39    wiek: 40,
40    statystyki: { waga: 56000, dlugosc: 25.0, wysokosc: 13.0 },
41    diety: ["rosliny"],
42    zagrozenie: 1,
43    lokalizacja: { wybieg: "Herbivore Valley", koordynaty: { x: 20.0, y: 45.0 } },
44    zachowania: [
45      { typ: "pasienie", czestosc: "wysoka", pory: ["rano", "poludnie", "wieczor"] }
46    ],
47    aktywny: true
48  },
49  {
50    id: "dino-004",
51    nazwa: "Spike",
52    gatunek: "Triceratops",
53    wiek: 15,
54    statystyki: { waga: 9000, dlugosc: 9.0, wysokosc: 3.0 },
55    diety: ["rosliny"],
56    zagrozenie: 2,
57    lokalizacja: { wybieg: "Herbivore Valley", koordynaty: { x: 22.5, y: 42.1 } },
58    zachowania: [
59      { typ: "pasienie", czestosc: "wysoka", pory: ["rano", "poludnie"] },
60      { typ: "obrona", czestosc: "niska", pory: ["noc"] }
61    ],
62    aktywny: true
63  },
64  {
65    id: "dino-005",
66    nazwa: "Delta",
67    gatunek: "Velociraptor",
68    wiek: 6,
69    statystyki: { waga: 130, dlugosc: 2.8, wysokosc: 1.6 },
70    diety: ["mieso"],
71    zagrozenie: 4,
72    lokalizacja: { wybieg: "Raptor Paddock", koordynaty: { x: 51.0, y: 31.0 } },
73    zachowania: [
74      { typ: "polowanie", czestosc: "wysoka", pory: ["noc"] }
75    ],
76    aktywny: true
77  }
78];
79
80// === TODO: Cwiczenia do wykonania ===
81
82// 1. Serializacja i deserializacja JSON
83const jsonString = JSON.stringify(dinozaury, null, 2);
84console.log("=== Serializacja JSON ===");
85console.log("Liczba znakow:", jsonString.length);
86const parsedDino = JSON.parse(jsonString);
87console.log("Sparsowane dinozaury:", parsedDino.length);
88
89// 2. TODO: Filtrowanie - znajdz wszystkie drapiezniki
90const drapiezniki = dinozaury.filter(d => d.diety.includes("mieso"));
91console.log("\n=== Drapiezniki ===");
92drapiezniki.forEach(d => console.log(d.nazwa, "-", d.gatunek));
93
94// 3. TODO: Mapowanie - stworz uproszczona liste
95const listaUproszczona = dinozaury.map(d => ({
96  nazwa: d.nazwa,
97  gatunek: d.gatunek,
98  waga_ton: d.statystyki.waga / 1000,
99  niebezpieczny: d.zagrozenie >= 4
100}));
101console.log("\n=== Lista uproszczona ===");
102console.log(JSON.stringify(listaUproszczona, null, 2));
103
104// 4. TODO: Redukcja - oblicz srednia wage
105const sredniaWaga = dinozaury.reduce(
106  (suma, d) => suma + d.statystyki.waga, 0
107) / dinozaury.length;
108console.log("\n=== Srednia waga ===");
109console.log(sredniaWaga.toFixed(1), "kg");
110
111// 5. TODO: Grupowanie po wybiegach
112const poWybiegach = dinozaury.reduce((grupy, d) => {
113  const wybieg = d.lokalizacja.wybieg;
114  if (!grupy[wybieg]) grupy[wybieg] = [];
115  grupy[wybieg].push(d.nazwa);
116  return grupy;
117}, {});
118console.log("\n=== Dinozaury w wybiegach ===");
119Object.entries(poWybiegach).forEach(([wybieg, nazwy]) =>
120  console.log(wybieg + ":", nazwy.join(", "))
121);
122
123// 6. TODO: Lancuch metod - top 3 najciezsze
124const top3 = dinozaury
125  .sort((a, b) => b.statystyki.waga - a.statystyki.waga)
126  .slice(0, 3)
127  .map(d => d.nazwa + " (" + (d.statystyki.waga / 1000) + "t)");
128console.log("\n=== Top 3 najciezsze ===");
129top3.forEach((info, i) => console.log((i + 1) + ".", info));
130
131// 7. Dodaj wlasne przetwarzanie danych!
132// Np. znajdz dinozaura o najdluzszym ciele, policz drapiezniki vs roslinozerce, itd.

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co robi metoda JSON.parse()?

  2. 2. Co robi trzeci argument w JSON.stringify(obj, null, 2)?

Zadania praktyczne w grze

  • Klikanie w kolejności

    Ułóż elementy pobierania i parsowania JSON z API:

  • Układanie w poziomie

    Ułóż elementy tworzenia Promise:

  • Układanie w poziomie

    Ułóż elementy obsługi Promise z then:

  • Układanie w pionie

    Try-catch

Przydatne artykuły