Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Projekt końcowy: System monitorowania dinozaurów

Witaj ponownie w centrum dowodzenia Parku Jurajskiego! W tym module poznałeś funkcje, tablice, zasięg zmiennych, hoisting, słowo kluczowe

this
, obiekty i prototypy. Teraz nadszedł czas, aby połączyć tę wiedzę w jeden, zintegrowany System Monitorowania Dinozaurów - serce operacyjne całego parku.

Wyobraź sobie centralną konsolę, na której operatorzy parku śledzą w czasie rzeczywistym lokalizację, stan zdrowia i zachowanie każdego dinozaura. Czujniki rozmieszczone po całym parku przesyłają dane, które nasz system musi przetwarzać, filtrować i analizować. Bez sprawnego systemu monitorowania, park szybko wymknąłby się spod kontroli - tak jak w filmie!

W tym projekcie stworzysz kompletny system, który wykorzystuje funkcje strzałkowe do przetwarzania danych, metody tablicowe do filtrowania i transformacji, funkcje wyższego rzędu do elastycznej logiki, a obiekty do modelowania encji parku.

Cel projektu

Zbudujesz system monitorowania, który:

  • Przechowuje dane o dinozaurach w tablicach obiektów
  • Używa funkcji z różnymi typami parametrów (domyślne, rest)
  • Stosuje funkcje strzałkowe do zwięzłego kodu
  • Filtruje, mapuje i redukuje dane za pomocą metod tablicowych
  • Prawidłowo zarządza zasięgiem zmiennych
  • Wykorzystuje obiekty z metodami do modelowania systemu

Wymagania funkcjonalne

1. Baza danych dinozaurów - tablice i obiekty

Pierwszym krokiem jest stworzenie bazy danych parku. Każdy dinozaur jest obiektem z właściwościami, a cała populacja przechowywana jest w tablicy.

1// Baza danych dinozaurów parku
2const dinosaurs = [
3  {
4    id: 1,
5    name: "Rex",
6    species: "Tyrannosaurus Rex",
7    weight: 8500,
8    diet: "carnivore",
9    enclosure: "A-1",
10    isHealthy: true,
11    heartRate: 72,
12    lastFed: "2024-03-15 08:00"
13  },
14  {
15    id: 2,
16    name: "Tricia",
17    species: "Triceratops",
18    weight: 6200,
19    diet: "herbivore",
20    enclosure: "B-3",
21    isHealthy: true,
22    heartRate: 55,
23    lastFed: "2024-03-15 07:30"
24  },
25  {
26    id: 3,
27    name: "Blue",
28    species: "Velociraptor",
29    weight: 75,
30    diet: "carnivore",
31    enclosure: "C-2",
32    isHealthy: false,
33    heartRate: 110,
34    lastFed: "2024-03-14 18:00"
35  },
36  {
37    id: 4,
38    name: "Dippy",
39    species: "Diplodocus",
40    weight: 12000,
41    diet: "herbivore",
42    enclosure: "D-1",
43    isHealthy: true,
44    heartRate: 40,
45    lastFed: "2024-03-15 06:00"
46  },
47  {
48    id: 5,
49    name: "Spike",
50    species: "Stegosaurus",
51    weight: 3500,
52    diet: "herbivore",
53    enclosure: "B-2",
54    isHealthy: true,
55    heartRate: 48,
56    lastFed: "2024-03-15 07:00"
57  }
58];
59
60// Dodawanie nowego dinozaura do bazy
61const addDinosaur = (name, species, weight, diet, enclosure) => {
62  const newId = dinosaurs.length + 1;
63  const newDino = {
64    id: newId,
65    name,
66    species,
67    weight,
68    diet,
69    enclosure,
70    isHealthy: true,
71    heartRate: 60,
72    lastFed: new Date().toISOString()
73  };
74  dinosaurs.push(newDino);
75  console.log("Zarejestrowano:", newDino.name, "(" + newDino.species + ")");
76  return newDino;
77};
78
79addDinosaur("Echo", "Velociraptor", 68, "carnivore", "C-2");
80console.log("Łączna liczba dinozaurów:", dinosaurs.length);

2. Filtrowanie i analiza - metody tablicowe

System musi umożliwiać szybkie filtrowanie populacji według różnych kryteriów. Tu wykorzystasz

filter
,
map
,
reduce
i
find
.

1// Filtrowanie drapieżników za pomocą filter
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Drapieżniki:", carnivores.map(d => d.name));
4
5// Filtrowanie chorych dinozaurów
6const unhealthyDinos = dinosaurs.filter(dino => !dino.isHealthy);
7console.log("Wymagają leczenia:", unhealthyDinos.map(d => d.name));
8
9// Mapowanie - tworzenie raportu z imionami i wagami
10const weightReport = dinosaurs.map(dino => {
11  return {
12    name: dino.name,
13    weight: dino.weight,
14    category: dino.weight > 5000 ? "ciężki" : "lekki"
15  };
16});
17console.log("Raport wagowy:");
18weightReport.forEach(entry => {
19  console.log("  " + entry.name + ": " + entry.weight + " kg (" + entry.category + ")");
20});
21
22// Redukcja - obliczanie łącznej wagi wszystkich dinozaurów
23const totalWeight = dinosaurs.reduce((sum, dino) => sum + dino.weight, 0);
24console.log("Łączna waga populacji:", totalWeight, "kg");
25
26// Średnia waga
27const avgWeight = totalWeight / dinosaurs.length;
28console.log("Średnia waga:", Math.round(avgWeight), "kg");
29
30// Znajdowanie najcięższego dinozaura
31const heaviest = dinosaurs.reduce((max, dino) =>
32  dino.weight > max.weight ? dino : max
33);
34console.log("Najcięższy:", heaviest.name, "-", heaviest.weight, "kg");
35
36// find - szukanie konkretnego dinozaura
37const blue = dinosaurs.find(dino => dino.name === "Blue");
38console.log("Znaleziono:", blue ? blue.name + " w " + blue.enclosure : "nie znaleziono");
39
40// some i every - sprawdzanie warunków
41const allHealthy = dinosaurs.every(dino => dino.isHealthy);
42const anyUnhealthy = dinosaurs.some(dino => !dino.isHealthy);
43console.log("Wszystkie zdrowe:", allHealthy);
44console.log("Są chore dinozaury:", anyUnhealthy);

3. Funkcje monitorujące - parametry, scope i this

System potrzebuje zestawu funkcji monitorujących z różnymi typami parametrów. Zwróć uwagę na zasięg zmiennych i różnicę między funkcjami zwykłymi a strzałkowymi w kontekście

this
.

1// Funkcja z parametrami domyślnymi
2function generateAlert(dinoName, alertType = "INFO", priority = "normal") {
3  const timestamp = new Date().toLocaleTimeString();
4  console.log("[" + timestamp + "] [" + alertType + "] [" + priority + "] " + dinoName);
5}
6
7generateAlert("Rex", "WARNING", "high");
8generateAlert("Tricia"); // użyje domyślnych wartości
9generateAlert("Blue", "DANGER"); // priority będzie "normal"
10
11// Funkcja z parametrem rest - rejestracja wielu zdarzeń
12const logEvents = (enclosure, ...events) => {
13  console.log("Wybieg " + enclosure + " - zarejestrowano " + events.length + " zdarzeń:");
14  events.forEach((event, index) => {
15    console.log("  " + (index + 1) + ". " + event);
16  });
17};
18
19logEvents("A-1", "Karmienie o 8:00", "Badanie weterynaryjne", "Aktywność ruchowa");
20logEvents("C-2", "Próba ucieczki!");
21
22// Obiekt monitora z metodami - this w kontekście
23const enclosureMonitor = {
24  sector: "A",
25  alertCount: 0,
26
27  // Metoda tradycyjna - this wskazuje na obiekt
28  checkFence: function() {
29    this.alertCount++;
30    console.log("Sprawdzanie ogrodzenia sektora " + this.sector +
31                " (alert #" + this.alertCount + ")");
32  },
33
34  // Metoda skrócona ES6
35  getStatus() {
36    return "Sektor " + this.sector + " - " + this.alertCount + " alertów";
37  }
38};
39
40enclosureMonitor.checkFence();
41enclosureMonitor.checkFence();
42console.log(enclosureMonitor.getStatus());
43
44// Zasięg zmiennych - blokowy vs funkcyjny
45function monitorHeartRates() {
46  const threshold = 100; // dostępna w całej funkcji
47
48  for (let i = 0; i < dinosaurs.length; i++) {
49    const dino = dinosaurs[i]; // dostępna tylko w tym bloku
50    if (dino.heartRate > threshold) {
51      let status = "PODWYŻSZONE"; // dostępna tylko w tym if
52      console.log(dino.name + ": tętno " + dino.heartRate + " - " + status);
53    }
54  }
55
56  // console.log(status); // BŁĄD! status nie istnieje poza blokiem if
57  // console.log(dino);   // BŁĄD! dino nie istnieje poza pętlą for
58  console.log("Próg alertu:", threshold); // OK - threshold jest w zakresie funkcji
59}
60
61monitorHeartRates();

4. Zaawansowane przetwarzanie - łączenie metod

W prawdziwym systemie monitorowania często musimy połączyć wiele operacji w łańcuch. Metody tablicowe świetnie się do tego nadają.

1// Łańcuch metod - raport bezpieczeństwa
2const securityReport = dinosaurs
3  .filter(dino => dino.diet === "carnivore") // tylko drapieżniki
4  .map(dino => ({                            // transformacja danych
5    name: dino.name,
6    enclosure: dino.enclosure,
7    danger: dino.weight > 1000 ? "WYSOKIE" : "ŚREDNIE"
8  }))
9  .sort((a, b) => a.name.localeCompare(b.name)); // sortowanie
10
11console.log("=== RAPORT BEZPIECZEŃSTWA ===");
12securityReport.forEach(entry => {
13  console.log(entry.name + " [" + entry.enclosure + "] - zagrożenie: " + entry.danger);
14});
15
16// Grupowanie dinozaurów według diety za pomocą reduce
17const groupedByDiet = dinosaurs.reduce((groups, dino) => {
18  const key = dino.diet;
19  if (!groups[key]) {
20    groups[key] = [];
21  }
22  groups[key].push(dino.name);
23  return groups;
24}, {});
25
26console.log("=== PODZIAŁ WEDŁUG DIETY ===");
27console.log("Roślinożercy:", groupedByDiet.herbivore);
28console.log("Mięsożercy:", groupedByDiet.carnivore);
29
30// Funkcja wyższego rzędu - tworzenie filtrów
31function createFilter(property, value) {
32  return (dino) => dino[property] === value;
33}
34
35const isCarnivore = createFilter("diet", "carnivore");
36const isInSectorB = createFilter("enclosure", "B-3");
37
38const carnivoreDinos = dinosaurs.filter(isCarnivore);
39const sectorBDinos = dinosaurs.filter(isInSectorB);
40
41console.log("Mięsożercy:", carnivoreDinos.map(d => d.name));
42console.log("Sektor B-3:", sectorBDinos.map(d => d.name));
43
44// Statystyki populacji
45const stats = {
46  total: dinosaurs.length,
47  healthy: dinosaurs.filter(d => d.isHealthy).length,
48  unhealthy: dinosaurs.filter(d => !d.isHealthy).length,
49  avgHeartRate: Math.round(
50    dinosaurs.reduce((sum, d) => sum + d.heartRate, 0) / dinosaurs.length
51  ),
52  heaviest: dinosaurs.reduce((max, d) => d.weight > max.weight ? d : max).name,
53  lightest: dinosaurs.reduce((min, d) => d.weight < min.weight ? d : min).name
54};
55
56console.log("=== STATYSTYKI POPULACJI ===");
57console.log("Łącznie:", stats.total);
58console.log("Zdrowe:", stats.healthy);
59console.log("Chore:", stats.unhealthy);
60console.log("Średnie tętno:", stats.avgHeartRate, "bpm");
61console.log("Najcięższy:", stats.heaviest);
62console.log("Najlżejszy:", stats.lightest);

Wskazówki implementacyjne

Budując swój system monitorowania, zwróć uwagę na:

  1. Funkcje strzałkowe vs zwykłe - używaj strzałkowych dla callbacków (
    filter
    ,
    map
    ,
    reduce
    ), a zwykłych dla metod obiektów, które potrzebują
    this
  2. Parametry domyślne - przydatne gdy nie zawsze mamy wszystkie dane (np. priorytet alertu)
  3. Parametr rest (
    ...args
    )
    - idealny gdy liczba argumentów jest zmienna (np. lista zdarzeń)
  4. Łańcuchowanie metod -
    filter().map().sort()
    jest czytelniejsze niż zagnieżdżone pętle
  5. Zasięg zmiennych -
    let
    i
    const
    mają zasięg blokowy,
    var
    funkcyjny - wybieraj świadomie
  6. Metody tablicowe nie mutują oryginału -
    filter
    ,
    map
    ,
    reduce
    tworzą nowe tablice, ale
    push
    ,
    splice
    ,
    sort
    modyfikują oryginał
  7. Obiekty jako struktury danych - grupuj powiązane dane w obiekty zamiast trzymać je w osobnych zmiennych

Powodzenia!

Czas wcielić się w rolę głównego operatora Parku Jurajskiego! Stwórz swój własny System Monitorowania w edytorze poniżej. Połącz tablice, funkcje, metody tablicowe i obiekty w jeden, działający system. Pamiętaj - każdy dinozaur w parku zależy od Twojego kodu. Jak mówił John Hammond: "Nie szczędziliśmy wydatków" - i Ty nie szczędź kodu!

Przejdź do CodeWorlds