Kurs JavaScript i TypeScript · Moduł 2: Funkcje, tablice i obiekty

Podstawowe metody tablic w JavaScript

18 min czytania
W tej lekcji3

Witaj ponownie w Parku Jurajskim! W poprzedniej lekcji poznaliśmy podstawy tablic w JavaScript. Teraz zagłębimy się w podstawowe metody tablic, które są niezbędne do codziennej pracy z danymi. Te metody pozwalają na dodawanie, usuwanie i manipulowanie elementami w tablicach, co jest kluczowe przy zarządzaniu inwentarzem dinozaurów, planowaniu karmienia czy organizacji personelu parku.

Modyfikacja tablic - metody zmieniające tablicę

Najpierw przyjrzyjmy się metodom, które modyfikują oryginalną tablicę. Są to tak zwane metody mutujące (ang. mutating methods).

1. push() - dodawanie elementów na końcu tablicy

Metoda push() dodaje jeden lub więcej elementów na końcu tablicy i zwraca nową długość tablicy:

1// Zarządzanie gatunkami dinozaurów w naszej zagrodzie
2const dinoEnclosure = ["T-Rex", "Velociraptor"];
3
4// Dodajemy nowy gatunek dinozaura
5dinoEnclosure.push("Dilophosaurus");
6console.log(dinoEnclosure); // ["T-Rex", "Velociraptor", "Dilophosaurus"]
7
8// Dodajemy kilka gatunków naraz
9const newLength = dinoEnclosure.push("Gallimimus", "Compsognathus");
10console.log(dinoEnclosure);
11// ["T-Rex", "Velociraptor", "Dilophosaurus", "Gallimimus", "Compsognathus"]
12console.log(`Aktualna liczba gatunków w zagrodzie: ${newLength}`); // 5

Kiedy warto używać push():

  • Gdy potrzebujesz dodać element(y) na końcu tablicy
  • Gdy chcesz śledzić nową długość tablicy po dodaniu elementów
  • Gdy kolejność elementów jest istotna, a nowy element powinien znaleźć się na końcu

2. pop() - usuwanie ostatniego elementu

Metoda pop() usuwa ostatni element z tablicy i zwraca ten element:

1// Lista dinozaurów w naszej zagrodzie
2const dinoEnclosure = ["T-Rex", "Velociraptor", "Dilophosaurus", "Compsognathus"];
3
4// Przenoszenie ostatniego dinozaura do nowej zagrody
5const removedDino = dinoEnclosure.pop();
6console.log(`Przeniesiono dinozaura: ${removedDino}`); // "Compsognathus"
7console.log("Pozostałe dinozaury:", dinoEnclosure); // ["T-Rex", "Velociraptor", "Dilophosaurus"]
8
9// Możemy kontynuować usuwanie
10const anotherRemovedDino = dinoEnclosure.pop();
11console.log(`Przeniesiono kolejnego dinozaura: ${anotherRemovedDino}`); // "Dilophosaurus"
12console.log("Aktualna zawartość zagrody:", dinoEnclosure); // ["T-Rex", "Velociraptor"]

Kiedy warto używać pop():

  • Gdy potrzebujesz usunąć ostatni element z tablicy
  • Gdy potrzebujesz pobrać wartość usuwanego elementu
  • Implementując struktury danych typu stos (LIFO - Last In, First Out)

3. unshift() - dodawanie elementów na początku

Metoda unshift() dodaje jeden lub więcej elementów na początku tablicy i zwraca nową długość tablicy:

1// Kolejka dinozaurów oczekujących na badania
2const dinoQueue = ["Velociraptor", "Triceratops"];
3
4// Priorytetowe dodanie T-Rexa na początek kolejki
5dinoQueue.unshift("T-Rex");
6console.log("Aktualna kolejka:", dinoQueue); // ["T-Rex", "Velociraptor", "Triceratops"]
7
8// Dodanie kilku dinozaurów naraz na początek
9const newQueueSize = dinoQueue.unshift("Brachiosaurus", "Stegosaurus");
10console.log("Aktualna kolejka:", dinoQueue);
11// ["Brachiosaurus", "Stegosaurus", "T-Rex", "Velociraptor", "Triceratops"]
12console.log(`Liczba dinozaurów w kolejce: ${newQueueSize}`); // 5

Kiedy warto używać unshift():

  • Gdy chcesz dodać element na początek tablicy
  • Implementując kolejki priorytetowe, gdzie elementy z wyższym priorytetem idą na początek
  • Gdy potrzebujesz śledzić nową długość tablicy

4. shift() - usuwanie pierwszego elementu

Metoda shift() usuwa pierwszy element z tablicy i zwraca ten element:

1// Kolejka dinozaurów oczekujących na badania
2const dinoQueue = ["T-Rex", "Velociraptor", "Triceratops", "Stegosaurus"];
3
4// Pobieramy pierwszego dinozaura do badań
5const nextDino = dinoQueue.shift();
6console.log(`Badamy dinozaura: ${nextDino}`); // "T-Rex"
7console.log("Pozostała kolejka:", dinoQueue); // ["Velociraptor", "Triceratops", "Stegosaurus"]
8
9// Obsługa kolejnego dinozaura
10const anotherDino = dinoQueue.shift();
11console.log(`Następny dinozaur do badań: ${anotherDino}`); // "Velociraptor"
12console.log("Aktualny stan kolejki:", dinoQueue); // ["Triceratops", "Stegosaurus"]

Kiedy warto używać shift():

  • Gdy potrzebujesz usunąć i pobrać pierwszy element tablicy
  • Implementując kolejki (FIFO - First In, First Out)
  • Przetwarzając elementy w kolejności ich dodania

5. splice() - modyfikacja elementów w dowolnym miejscu

Metoda splice() zmienia zawartość tablicy, usuwając, zamieniając lub dodając elementy. Jest niezwykle wszechstronna, ponieważ pozwala na modyfikację tablicy w dowolnym miejscu.

Podstawowa składnia: array.splice(startIndex, deleteCount, item1, item2, ...):

  • startIndex: indeks, od którego zacząć zmianę
  • deleteCount: liczba elementów do usunięcia
  • item1, item2, ...: (opcjonalnie) elementy do dodania

5.1. Usuwanie elementów za pomocą splice()

1// Lista gatunków dinozaurów w parku
2const dinosaurs = ["T-Rex", "Velociraptor", "Triceratops", "Stegosaurus", "Brachiosaurus"];
3
4// Usuwamy 2 elementy zaczynając od indeksu 1 (drugiego elementu)
5const removedDinos = dinosaurs.splice(1, 2);
6console.log("Usunięte dinozaury:", removedDinos); // ["Velociraptor", "Triceratops"]
7console.log("Pozostałe dinozaury:", dinosaurs); // ["T-Rex", "Stegosaurus", "Brachiosaurus"]

5.2. Zastępowanie elementów za pomocą splice()

1// Lista gatunków dinozaurów
2const dinosaurs = ["T-Rex", "Dilophosaurus", "Triceratops", "Stegosaurus"];
3
4// Zastępujemy Dilophosaurus przez Velociraptor
5const replaced = dinosaurs.splice(1, 1, "Velociraptor");
6console.log("Zastąpiony dinozaur:", replaced); // ["Dilophosaurus"]
7console.log("Aktualna lista:", dinosaurs); // ["T-Rex", "Velociraptor", "Triceratops", "Stegosaurus"]
8
9// Zastępujemy wiele elementów naraz
10dinosaurs.splice(2, 2, "Brachiosaurus", "Ankylosaurus", "Parasaurolophus");
11console.log("Nowy skład:", dinosaurs);
12// ["T-Rex", "Velociraptor", "Brachiosaurus", "Ankylosaurus", "Parasaurolophus"]

5.3. Dodawanie elementów bez usuwania

1// Lista gatunków dinozaurów
2const dinosaurs = ["T-Rex", "Velociraptor"];
3
4// Dodajemy nowe gatunki na indeksie 1 (między T-Rex a Velociraptor)
5dinosaurs.splice(1, 0, "Dilophosaurus", "Compsognathus");
6console.log(dinosaurs); // ["T-Rex", "Dilophosaurus", "Compsognathus", "Velociraptor"]

Kiedy warto używać splice():

  • Gdy potrzebujesz modyfikować tablicę w określonym miejscu, a nie tylko na końcu czy początku
  • Gdy potrzebujesz usunąć i/lub dodać elementy jednocześnie
  • Gdy chcesz zastąpić sekcję tablicy nowymi wartościami

6. sort() - sortowanie tablicy

Metoda sort() sortuje elementy tablicy na miejscu i zwraca posortowaną tablicę:

1// Lista dinozaurów do karmienia
2const feedingList = ["Velociraptor", "T-Rex", "Dilophosaurus", "Gallimimus", "Compsognathus"];
3
4// Sortowanie alfabetyczne
5feedingList.sort();
6console.log("Lista karmienia (alfabetycznie):", feedingList);
7// ["Compsognathus", "Dilophosaurus", "Gallimimus", "T-Rex", "Velociraptor"]
8
9// Tablica z wymiarami dinozaurów (w cm)
10const dinoSizes = [1200, 300, 800, 2000, 40];
11
12// Domyślne sortowanie traktuje liczby jako stringi - to nie zadziała poprawnie!
13dinoSizes.sort();
14console.log("Niepoprawnie posortowane wymiary:", dinoSizes);
15// Prawdopodobnie: [1200, 2000, 300, 40, 800] - niepoprawnie!
16
17// Właściwe sortowanie liczb z funkcją porównującą
18dinoSizes.sort((a, b) => a - b);
19console.log("Poprawnie posortowane wymiary (rosnąco):", dinoSizes);
20// [40, 300, 800, 1200, 2000]
21
22// Sortowanie malejąco
23dinoSizes.sort((a, b) => b - a);
24console.log("Wymiary posortowane malejąco:", dinoSizes);
25// [2000, 1200, 800, 300, 40]

Sortowanie obiektów

1// Dane dinozaurów do karmienia
2const dinosaurs = [
3  { name: "T-Rex", weight: 7000, priority: 1 },
4  { name: "Velociraptor", weight: 100, priority: 2 },
5  { name: "Triceratops", weight: 9000, priority: 3 },
6  { name: "Gallimimus", weight: 450, priority: 4 }
7];
8
9// Sortowanie według priorytetu karmienia
10dinosaurs.sort((a, b) => a.priority - b.priority);
11console.log("Kolejność karmienia według priorytetu:");
12dinosaurs.forEach(dino => console.log(`${dino.name} (priorytet: ${dino.priority})`));
13
14// Sortowanie według wagi (od najcięższego)
15dinosaurs.sort((a, b) => b.weight - a.weight);
16console.log("\nKolejność według wagi (od najcięższego):");
17dinosaurs.forEach(dino => console.log(`${dino.name} (${dino.weight} kg)`));
18
19// Sortowanie alfabetycznie według nazwy
20dinosaurs.sort((a, b) => a.name.localeCompare(b.name));
21console.log("\nDinozaury alfabetycznie:");
22dinosaurs.forEach(dino => console.log(`${dino.name}`));

Metoda sort() przyjmuje opcjonalną funkcję porównującą, która powinna zwrócić:

  • Wartość ujemną, jeśli a powinno być przed b
  • Zero, jeśli a i b są równoważne pod względem sortowania
  • Wartość dodatnią, jeśli a powinno być po b

Kiedy warto używać sort():

  • Gdy potrzebujesz uporządkować elementy tablicy według określonego kryterium
  • Gdy pracujesz z listami, które muszą być prezentowane w określonej kolejności
  • Gdy implementujesz funkcjonalność sortowania w aplikacji

7. reverse() - odwrócenie kolejności tablicy

Metoda reverse() odwraca kolejność elementów w tablicy i zwraca odwróconą tablicę:

1// Lista dinozaurów od najstarszego do najmłodszego
2const dinosaursByAge = ["Brachiosaurus", "Stegosaurus", "Triceratops", "Velociraptor", "T-Rex"];
3
4// Odwrócenie kolejności, aby uzyskać listę od najmłodszego do najstarszego
5dinosaursByAge.reverse();
6console.log("Od najmłodszego do najstarszego:", dinosaursByAge);
7// ["T-Rex", "Velociraptor", "Triceratops", "Stegosaurus", "Brachiosaurus"]
8
9// Często używana w połączeniu z sort()
10const dinoHeights = [500, 900, 200, 1200, 350];
11// Sortowanie malejąco poprzez sortowanie rosnąco + odwrócenie
12dinoHeights.sort((a, b) => a - b).reverse();
13console.log("Wysokości malejąco:", dinoHeights);
14// [1200, 900, 500, 350, 200]

Kiedy warto używać reverse():

  • Gdy potrzebujesz odwrócić kolejność elementów w tablicy
  • Gdy chcesz zmienić porządek z rosnącego na malejący (lub odwrotnie) po sortowaniu
  • Gdy implementujesz funkcjonalność "odwróć kolejność" w interfejsie użytkownika

8. fill() - wypełnianie tablicy wartością

Metoda fill() wypełnia wszystkie elementy tablicy od indeksu startowego do indeksu końcowego (nie wliczając) określoną wartością:

1// Przygotowanie tablicy na pomiary temperatury dinozaurów (10 pomiarów)
2const temperatures = new Array(10);
3
4// Wypełnienie domyślną wartością (38.5 stopni Celsjusza)
5temperatures.fill(38.5);
6console.log("Domyślne temperatury:", temperatures);
7// [38.5, 38.5, 38.5, 38.5, 38.5, 38.5, 38.5, 38.5, 38.5, 38.5]
8
9// Wypełnienie tylko części tablicy
10temperatures.fill(39.2, 2, 5); // Od indeksu 2 (włącznie) do 5 (wyłączając)
11console.log("Zaktualizowane temperatury:", temperatures);
12// [38.5, 38.5, 39.2, 39.2, 39.2, 38.5, 38.5, 38.5, 38.5, 38.5]
13
14// Użycie z nową tablicą i indeksami ujemnymi (liczone od końca)
15const feedingSchedule = new Array(7).fill("Standardowe");
16feedingSchedule.fill("Zwiększona porcja", -3); // Ostatnie 3 dni
17console.log("Plan karmienia na tydzień:", feedingSchedule);
18// ["Standardowe", "Standardowe", "Standardowe", "Standardowe", "Zwiększona porcja", "Zwiększona porcja", "Zwiększona porcja"]

Kiedy warto używać fill():

  • Gdy tworzysz nową tablicę i potrzebujesz wypełnić ją określoną wartością początkową
  • Gdy potrzebujesz zastąpić wszystkie lub niektóre wartości w istniejącej tablicy
  • Gdy inicjalizujesz tablice do przechowywania danych z domyślnymi wartościami

Zastosowanie podstawowych metod tablic w praktyce

Teraz połączmy poznane metody, aby rozwiązać bardziej praktyczne scenariusze w naszym Parku Jurajskim:

Przykład 1: System zarządzania karmą dla dinozaurów

1// System zarządzania karmą dla dinozaurów
2const feedingSystem = {
3  // Stan systemu
4  foodInventory: [],
5  feedingQueue: [],
6  feedingHistory: [],
7
8  // Dodawanie nowego zapasu karmy
9  addFoodSupply: function(type, quantity, expirationDate) {
10    const supply = { type, quantity, expirationDate, addedOn: new Date() };
11    this.foodInventory.push(supply);
12
13    // Sortowanie według daty ważności (najkrótszej)
14    this.foodInventory.sort((a, b) =>
15      new Date(a.expirationDate) - new Date(b.expirationDate)
16    );
17
18    console.log(`Dodano ${quantity}kg karmy typu "${type}" do zapasów.`);
19    return supply;
20  },
21
22  // Dodawanie dinozaura do kolejki karmienia
23  scheduleDinoFeeding: function(dinoId, dinoName, foodType, quantity, priority) {
24    const feedingRequest = {
25      dinoId,
26      dinoName,
27      foodType,
28      quantity,
29      priority,
30      scheduledAt: new Date()
31    };
32
33    if (priority === "high") {
34      // Dodaj na początek kolejki
35      this.feedingQueue.unshift(feedingRequest);
36    } else {
37      // Dodaj na koniec kolejki
38      this.feedingQueue.push(feedingRequest);
39    }
40
41    console.log(`Zaplanowano karmienie dla ${dinoName} (priorytet: ${priority})`);
42    return this.feedingQueue.length;
43  },
44
45  // Pobieranie następnego dinozaura do nakarmienia
46  getNextDinoToFeed: function() {
47    if (this.feedingQueue.length === 0) {
48      console.log("Wszystkie dinozaury nakarmione. Kolejka pusta.");
49      return null;
50    }
51
52    const nextFeeding = this.feedingQueue.shift();
53    console.log(`Następny do karmienia: ${nextFeeding.dinoName}`);
54    return nextFeeding;
55  },
56
57  // Wydawanie karmy ze spichlerza
58  dispenseFoodForDino: function(dinoFeeding) {
59    const { foodType, quantity, dinoName } = dinoFeeding;
60
61    // Szukanie odpowiedniego zapasu karmy
62    let remainingQuantity = quantity;
63    const usedSupplies = [];
64
65    // Iterujemy od najstarszego zapasu (który wygasa najszybciej)
66    for (let i = 0; i < this.foodInventory.length && remainingQuantity > 0; i++) {
67      const supply = this.foodInventory[i];
68
69      if (supply.type === foodType && supply.quantity > 0) {
70        // Ile możemy pobrać z tego zapasu
71        const amountToTake = Math.min(remainingQuantity, supply.quantity);
72        supply.quantity -= amountToTake;
73        remainingQuantity -= amountToTake;
74
75        usedSupplies.push({
76          type: supply.type,
77          amount: amountToTake,
78          expirationDate: supply.expirationDate
79        });
80
81        // Jeśli zapas się skończył, usuwamy go z inwentarza
82        if (supply.quantity === 0) {
83          this.foodInventory.splice(i, 1);
84          i--; // Dostosuj indeks po usunięciu elementu
85        }
86      }
87    }
88
89    // Dodaj rekord do historii karmienia
90    const feedingRecord = {
91      ...dinoFeeding,
92      fedAt: new Date(),
93      suppliesUsed: usedSupplies,
94      fullyFed: remainingQuantity === 0
95    };
96
97    this.feedingHistory.push(feedingRecord);
98
99    if (remainingQuantity > 0) {
100      console.log(`UWAGA: Brakuje ${remainingQuantity}kg karmy typu "${foodType}" dla ${dinoName}!`);
101      return false;
102    }
103
104    console.log(`Nakarmiono ${dinoName} używając ${quantity}kg karmy typu "${foodType}".`);
105    return feedingRecord;
106  },
107
108  // Przeprowadzenie pełnego procesu karmienia
109  processFeedingCycle: function(maxFeedings = 5) {
110    console.log("=== ROZPOCZĘCIE CYKLU KARMIENIA ===");
111    let feedingsProcessed = 0;
112
113    // Przetwarzamy określoną liczbę karmień lub dopóki kolejka się nie opróżni
114    while (feedingsProcessed < maxFeedings && this.feedingQueue.length > 0) {
115      const nextDino = this.getNextDinoToFeed();
116
117      if (nextDino) {
118        const result = this.dispenseFoodForDino(nextDino);
119
120        if (!result) {
121          // Jeśli nie udało się nakarmić, przenosimy dinozaura na koniec kolejki
122          this.feedingQueue.push({
123            ...nextDino,
124            priority: "high", // Podnosimy priorytet dla nienakarmionego dinozaura
125            scheduledAt: new Date()
126          });
127          console.log(`${nextDino.dinoName} przesunięty na koniec kolejki z wysokim priorytetem.`);
128        }
129
130        feedingsProcessed++;
131      }
132    }
133
134    console.log(`=== ZAKOŃCZENIE CYKLU KARMIENIA (przetworzono: ${feedingsProcessed}) ===`);
135    console.log(`Pozostało w kolejce: ${this.feedingQueue.length} dinozaurów`);
136    return feedingsProcessed;
137  },
138
139  // Raportowanie stanu zapasów
140  reportInventoryStatus: function() {
141    console.log("=== RAPORT STANU ZAPASÓW ===");
142
143    if (this.foodInventory.length === 0) {
144      console.log("Magazyn jest pusty! Zamów nowe zapasy karmy!");
145      return { isEmpty: true, totalQuantity: 0, types: [] };
146    }
147
148    // Grupowanie zapasów według typu
149    const inventory = this.foodInventory.reduce((acc, supply) => {
150      if (!acc[supply.type]) {
151        acc[supply.type] = 0;
152      }
153      acc[supply.type] += supply.quantity;
154      return acc;
155    }, {});
156
157    // Obliczanie łącznej ilości
158    const totalQuantity = Object.values(inventory).reduce((sum, qty) => sum + qty, 0);
159
160    console.log(`Łączna ilość karmy: ${totalQuantity}kg`);
161    Object.entries(inventory).forEach(([type, qty]) => {
162      console.log(`- ${type}: ${qty}kg`);
163    });
164
165    // Sprawdzenie zapasów, które wkrótce wygasną (w ciągu 3 dni)
166    const today = new Date();
167    const threeDaysLater = new Date(today);
168    threeDaysLater.setDate(today.getDate() + 3);
169
170    const expiringSupplies = this.foodInventory.filter(supply =>
171      new Date(supply.expirationDate) <= threeDaysLater
172    );
173
174    if (expiringSupplies.length > 0) {
175      console.log("\nZapasy wygasające w ciągu 3 dni:");
176      expiringSupplies.forEach(supply => {
177        console.log(`- ${supply.quantity}kg ${supply.type} (wygasa: ${supply.expirationDate})`);
178      });
179    }
180
181    return {
182      isEmpty: false,
183      totalQuantity,
184      types: Object.keys(inventory),
185      inventory,
186      expiringSupplies
187    };
188  }
189};
190
191// Demonstracja działania systemu
192console.log("=== DEMONSTRACJA SYSTEMU ZARZĄDZANIA KARMĄ ===");
193
194// 1. Dodawanie zapasów karmy
195feedingSystem.addFoodSupply("Mięso", 500, "2023-07-15");
196feedingSystem.addFoodSupply("Rośliny", 800, "2023-07-10");
197feedingSystem.addFoodSupply("Ryby", 200, "2023-07-05");
198feedingSystem.addFoodSupply("Mięso", 300, "2023-07-20");
199
200// 2. Planowanie karmienia
201feedingSystem.scheduleDinoFeeding("TRX-01", "Rex", "Mięso", 150, "high");
202feedingSystem.scheduleDinoFeeding("VEL-02", "Blue", "Mięso", 50, "normal");
203feedingSystem.scheduleDinoFeeding("TRCP-03", "Trixie", "Rośliny", 120, "normal");
204feedingSystem.scheduleDinoFeeding("BRCH-04", "Bronty", "Rośliny", 250, "normal");
205feedingSystem.scheduleDinoFeeding("STEG-05", "Spike", "Rośliny", 100, "high");
206
207// 3. Raport stanu zapasów przed karmieniem
208feedingSystem.reportInventoryStatus();
209
210// 4. Przeprowadzanie cyklu karmienia
211feedingSystem.processFeedingCycle(3); // Przetwarzamy tylko 3 karmienia
212
213// 5. Sprawdzamy stan zapasów po karmieniu
214feedingSystem.reportInventoryStatus();
215
216// 6. Dokończenie cyklu karmienia
217feedingSystem.processFeedingCycle(5); // Przetwarzamy pozostałe karmienia
218
219// 7. Sprawdzamy stan zapasów po pełnym cyklu
220feedingSystem.reportInventoryStatus();

Przykład 2: Zarządzanie zespołami pracowników parku

1// System zarządzania zespołami pracowników Parku Jurajskiego
2const staffManagementSystem = {
3  // Dane systemu
4  departments: [
5    { id: "OPS", name: "Operacje" },
6    { id: "SCI", name: "Badania naukowe" },
7    { id: "SEC", name: "Bezpieczeństwo" },
8    { id: "VET", name: "Weterynaria" },
9    { id: "MAINT", name: "Konserwacja" }
10  ],
11  staff: [],
12  teams: [],
13
14  // Dodawanie nowego pracownika
15  addStaffMember: function(name, role, skills, departmentId) {
16    // Sprawdzenie, czy dział istnieje
17    const department = this.departments.find(dept => dept.id === departmentId);
18    if (!department) {
19      console.log(`Błąd: Dział o ID "${departmentId}" nie istnieje.`);
20      return null;
21    }
22
23    // Generowanie ID pracownika
24    const id = `${departmentId}-${(this.staff.length + 1).toString().padStart(3, '0')}`;
25
26    const staffMember = {
27      id,
28      name,
29      role,
30      skills: Array.isArray(skills) ? skills : [skills],
31      departmentId,
32      departmentName: department.name,
33      hireDate: new Date().toISOString().split('T')[0]
34    };
35
36    this.staff.push(staffMember);
37    console.log(`Dodano pracownika: ${name} (${role}) do działu ${department.name}`);
38
39    return staffMember;
40  },
41
42  // Utworzenie nowego zespołu
43  createTeam: function(name, mission, leaderId) {
44    // Sprawdzenie, czy leader istnieje
45    const leader = this.staff.find(person => person.id === leaderId);
46    if (!leader) {
47      console.log(`Błąd: Pracownik o ID "${leaderId}" nie istnieje.`);
48      return null;
49    }
50
51    const teamId = `TEAM-${(this.teams.length + 1).toString().padStart(3, '0')}`;
52
53    const team = {
54      id: teamId,
55      name,
56      mission,
57      createdAt: new Date().toISOString().split('T')[0],
58      leaderId,
59      leaderName: leader.name,
60      members: [leaderId],
61      status: "active"
62    };
63
64    this.teams.push(team);
65    console.log(`Utworzono zespół: ${name} (misja: ${mission}), prowadzący: ${leader.name}`);
66
67    return team;
68  },
69
70  // Dodawanie pracownika do zespołu
71  addMemberToTeam: function(teamId, memberId) {
72    // Znajdź zespół
73    const teamIndex = this.teams.findIndex(team => team.id === teamId);
74    if (teamIndex === -1) {
75      console.log(`Błąd: Zespół o ID "${teamId}" nie istnieje.`);
76      return false;
77    }
78
79    // Znajdź pracownika
80    const member = this.staff.find(person => person.id === memberId);
81    if (!member) {
82      console.log(`Błąd: Pracownik o ID "${memberId}" nie istnieje.`);
83      return false;
84    }
85
86    // Sprawdź, czy pracownik już jest w zespole
87    if (this.teams[teamIndex].members.includes(memberId)) {
88      console.log(`${member.name} już jest członkiem zespołu ${this.teams[teamIndex].name}.`);
89      return false;
90    }
91
92    // Dodaj pracownika do zespołu
93    this.teams[teamIndex].members.push(memberId);
94    console.log(`Dodano ${member.name} do zespołu ${this.teams[teamIndex].name}.`);
95
96    return true;
97  },
98
99  // Usunięcie pracownika z zespołu
100  removeMemberFromTeam: function(teamId, memberId) {
101    // Znajdź zespół
102    const teamIndex = this.teams.findIndex(team => team.id === teamId);
103    if (teamIndex === -1) {
104      console.log(`Błąd: Zespół o ID "${teamId}" nie istnieje.`);
105      return false;
106    }
107
108    // Sprawdź, czy pracownik jest liderem
109    if (this.teams[teamIndex].leaderId === memberId) {
110      console.log(`Błąd: Nie można usunąć lidera zespołu. Najpierw zmień lidera.`);
111      return false;
112    }
113
114    // Znajdź indeks pracownika w zespole
115    const memberIndex = this.teams[teamIndex].members.indexOf(memberId);
116    if (memberIndex === -1) {
117      console.log(`Pracownik nie jest członkiem zespołu ${this.teams[teamIndex].name}.`);
118      return false;
119    }
120
121    // Znajdź pracownika dla celów informacyjnych
122    const member = this.staff.find(person => person.id === memberId);
123
124    // Usuń pracownika z zespołu
125    this.teams[teamIndex].members.splice(memberIndex, 1);
126    console.log(`Usunięto ${member ? member.name : memberId} z zespołu ${this.teams[teamIndex].name}.`);
127
128    return true;
129  },
130
131  // Zmiana lidera zespołu
132  changeTeamLeader: function(teamId, newLeaderId) {
133    // Znajdź zespół
134    const teamIndex = this.teams.findIndex(team => team.id === teamId);
135    if (teamIndex === -1) {
136      console.log(`Błąd: Zespół o ID "${teamId}" nie istnieje.`);
137      return false;
138    }
139
140    // Sprawdź, czy nowy lider istnieje
141    const newLeader = this.staff.find(person => person.id === newLeaderId);
142    if (!newLeader) {
143      console.log(`Błąd: Pracownik o ID "${newLeaderId}" nie istnieje.`);
144      return false;
145    }
146
147    // Sprawdź, czy nowy lider jest członkiem zespołu
148    if (!this.teams[teamIndex].members.includes(newLeaderId)) {
149      // Dodaj nowego lidera do zespołu, jeśli jeszcze nie jest członkiem
150      this.teams[teamIndex].members.push(newLeaderId);
151      console.log(`${newLeader.name} został dodany do zespołu jako nowy lider.`);
152    }
153
154    // Zapisz informacje o starym liderze dla komunikatu
155    const oldLeaderId = this.teams[teamIndex].leaderId;
156    const oldLeader = this.staff.find(person => person.id === oldLeaderId);
157
158    // Zaktualizuj lidera
159    this.teams[teamIndex].leaderId = newLeaderId;
160    this.teams[teamIndex].leaderName = newLeader.name;
161
162    console.log(`Lider zespołu ${this.teams[teamIndex].name} zmieniony z ${oldLeader.name} na ${newLeader.name}.`);
163    return true;
164  },
165
166  // Rozwiązanie zespołu
167  deactivateTeam: function(teamId, reason) {
168    // Znajdź zespół
169    const teamIndex = this.teams.findIndex(team => team.id === teamId);
170    if (teamIndex === -1) {
171      console.log(`Błąd: Zespół o ID "${teamId}" nie istnieje.`);
172      return false;
173    }
174
175    // Zapisanie stanu przed modyfikacją
176    const teamName = this.teams[teamIndex].name;
177
178    // Aktualizacja statusu zespołu
179    this.teams[teamIndex].status = "inactive";
180    this.teams[teamIndex].deactivatedAt = new Date().toISOString().split('T')[0];
181    this.teams[teamIndex].deactivationReason = reason || "Nieokreślony";
182
183    console.log(`Zespół ${teamName} został dezaktywowany. Powód: ${this.teams[teamIndex].deactivationReason}`);
184    return true;
185  },
186
187  // Generowanie raportu o zespołach
188  generateTeamsReport: function(includeInactive = false) {
189    console.log("=== RAPORT O ZESPOŁACH PARKU JURAJSKIEGO ===");
190
191    // Filtruj zespoły według statusu, jeśli nie uwzględniamy nieaktywnych
192    const teamsToReport = includeInactive
193      ? this.teams
194      : this.teams.filter(team => team.status === "active");
195
196    if (teamsToReport.length === 0) {
197      console.log("Brak zespołów do wyświetlenia.");
198      return { teams: [] };
199    }
200
201    // Przygotowanie danych do raportu
202    const teamsData = teamsToReport.map(team => {
203      // Pobierz pełne informacje o członkach
204      const memberDetails = team.members.map(memberId => {
205        const member = this.staff.find(person => person.id === memberId);
206        return member ? {
207          id: member.id,
208          name: member.name,
209          role: member.role,
210          isLeader: memberId === team.leaderId
211        } : { id: memberId, name: "Nieznany", role: "Nieznany", isLeader: false };
212      });
213
214      return {
215        id: team.id,
216        name: team.name,
217        mission: team.mission,
218        status: team.status,
219        memberCount: team.members.length,
220        leader: this.staff.find(person => person.id === team.leaderId),
221        members: memberDetails,
222        createdAt: team.createdAt,
223        deactivatedAt: team.deactivatedAt,
224        deactivationReason: team.deactivationReason
225      };
226    });
227
228    // Sortuj zespoły - aktywne pierwsze, potem według daty utworzenia
229    teamsData.sort((a, b) => {
230      if (a.status !== b.status) {
231        return a.status === "active" ? -1 : 1;
232      }
233      return new Date(b.createdAt) - new Date(a.createdAt);
234    });
235
236    // Wyświetl podsumowanie
237    console.log(`Liczba zespołów: ${teamsData.length}`);
238    console.log(`Aktywne zespoły: ${teamsData.filter(t => t.status === "active").length}`);
239    if (includeInactive) {
240      console.log(`Nieaktywne zespoły: ${teamsData.filter(t => t.status === "inactive").length}`);
241    }
242
243    // Wyświetl szczegóły dla każdego zespołu
244    teamsData.forEach(team => {
245      console.log(`\n${team.id}: ${team.name} (Status: ${team.status})`);
246      console.log(`Misja: ${team.mission}`);
247      console.log(`Lider: ${team.leader ? team.leader.name : 'Brak'} (${team.leader ? team.leader.role : ''})`);
248      console.log(`Liczba członków: ${team.memberCount}`);
249      console.log("Członkowie:");
250      team.members.forEach(member => {
251        console.log(`  - ${member.name} (${member.role})${member.isLeader ? ' [LIDER]' : ''}`);
252      });
253
254      if (team.status === "inactive") {
255        console.log(`Dezaktywowano: ${team.deactivatedAt}`);
256        console.log(`Powód: ${team.deactivationReason}`);
257      }
258    });
259
260    return { teams: teamsData };
261  },
262
263  // Reorganizacja zespołów
264  reorganizeTeams: function(fromTeamId, toTeamId, memberIds, dissolveSourceTeam = false) {
265    // Sprawdź, czy zespoły istnieją
266    const fromTeamIndex = this.teams.findIndex(team => team.id === fromTeamId);
267    const toTeamIndex = this.teams.findIndex(team => team.id === toTeamId);
268
269    if (fromTeamIndex === -1 || toTeamIndex === -1) {
270      console.log(`Błąd: Jeden lub oba zespoły nie istnieją.`);
271      return false;
272    }
273
274    // Sprawdź, czy zespoły są aktywne
275    if (this.teams[fromTeamIndex].status !== "active" || this.teams[toTeamIndex].status !== "active") {
276      console.log(`Błąd: Zespoły muszą być aktywne, aby je reorganizować.`);
277      return false;
278    }
279
280    // Jeśli nie podano konkretnych członków, przenieś wszystkich (oprócz lidera)
281    const membersToMove = memberIds ||
282      this.teams[fromTeamIndex].members.filter(id => id !== this.teams[fromTeamIndex].leaderId);
283
284    // Przenieś członków
285    for (const memberId of membersToMove) {
286      // Sprawdź, czy pracownik jest częścią źródłowego zespołu
287      if (!this.teams[fromTeamIndex].members.includes(memberId)) {
288        console.log(`Błąd: Pracownik o ID ${memberId} nie jest członkiem zespołu źródłowego.`);
289        continue;
290      }
291
292      // Sprawdź, czy pracownik nie jest liderem (jeśli nie chcemy przenosić wszystkich)
293      if (memberIds && memberId === this.teams[fromTeamIndex].leaderId) {
294        console.log(`Nie można przenieść lidera zespołu. Najpierw zmień lidera.`);
295        continue;
296      }
297
298      // Sprawdź, czy pracownik już jest częścią docelowego zespołu
299      if (this.teams[toTeamIndex].members.includes(memberId)) {
300        console.log(`Pracownik o ID ${memberId} już jest członkiem zespołu docelowego.`);
301        continue;
302      }
303
304      // Usuń z pierwotnego zespołu
305      const fromIndex = this.teams[fromTeamIndex].members.indexOf(memberId);
306      this.teams[fromTeamIndex].members.splice(fromIndex, 1);
307
308      // Dodaj do nowego zespołu
309      this.teams[toTeamIndex].members.push(memberId);
310
311      // Znajdź nazwę pracownika dla logów
312      const member = this.staff.find(person => person.id === memberId);
313      console.log(`Przeniesiono ${member ? member.name : memberId} z zespołu ${this.teams[fromTeamIndex].name} do ${this.teams[toTeamIndex].name}`);
314    }
315
316    // Jeśli wybrano rozwiązanie źródłowego zespołu i nie ma już członków
317    if (dissolveSourceTeam || this.teams[fromTeamIndex].members.length === 0) {
318      this.deactivateTeam(fromTeamId, "Reorganizacja - zespół przeniesiony");
319    }
320
321    return true;
322  }
323};
324
325// Demonstracja działania systemu zarządzania zespołami
326console.log("=== DEMONSTRACJA SYSTEMU ZARZĄDZANIA ZESPOŁAMI ===");
327
328// 1. Dodawanie pracowników
329const owen = staffManagementSystem.addStaffMember("Owen Grady", "Trener dinozaurów", ["Zachowania zwierząt", "Tresura", "Pierwsza pomoc"], "OPS");
330const claire = staffManagementSystem.addStaffMember("Claire Dearing", "Dyrektor operacyjny", ["Zarządzanie", "Marketing", "Administracja"], "OPS");
331const dr_wu = staffManagementSystem.addStaffMember("Dr. Henry Wu", "Główny genetyk", ["Genetyka", "Bioinżynieria", "Klonowanie"], "SCI");
332const barry = staffManagementSystem.addStaffMember("Barry", "Asystent trenera", ["Tresura", "Opieka nad zwierzętami"], "OPS");
333const lowery = staffManagementSystem.addStaffMember("Lowery Cruthers", "Technik systemów", ["IT", "Monitoring", "Komunikacja"], "SEC");
334const vivian = staffManagementSystem.addStaffMember("Vivian Krill", "Koordynator bezpieczeństwa", ["Bezpieczeństwo", "Komunikacja", "Zarządzanie kryzysowe"], "SEC");
335const muldoon = staffManagementSystem.addStaffMember("Robert Muldoon", "Specjalista ds. bezpieczeństwa", ["Broń", "Bezpieczeństwo", "Tropienie"], "SEC");
336const malcolm = staffManagementSystem.addStaffMember("Dr. Ian Malcolm", "Matematyk", ["Teoria chaosu", "Analiza ryzyka", "Modelowanie"], "SCI");
337const sattler = staffManagementSystem.addStaffMember("Dr. Ellie Sattler", "Paleobotanik", ["Botanika", "Paleontologia", "Ekologia"], "SCI");
338const harding = staffManagementSystem.addStaffMember("Dr. Gerry Harding", "Weterynarz", ["Weterynaria", "Zoologia", "Medycyna"], "VET");
339
340// 2. Tworzenie zespołów
341const raptorTeam = staffManagementSystem.createTeam("Zespół Velociraptor", "Tresura i opieka nad Velociraptorami", owen.id);
342staffManagementSystem.addMemberToTeam(raptorTeam.id, barry.id);
343
344const securityTeam = staffManagementSystem.createTeam("Zespół Bezpieczeństwa", "Zapewnienie bezpieczeństwa w parku", muldoon.id);
345staffManagementSystem.addMemberToTeam(securityTeam.id, vivian.id);
346staffManagementSystem.addMemberToTeam(securityTeam.id, lowery.id);
347
348const scienceTeam = staffManagementSystem.createTeam("Zespół Naukowy", "Badania naukowe i rozwój", dr_wu.id);
349staffManagementSystem.addMemberToTeam(scienceTeam.id, malcolm.id);
350staffManagementSystem.addMemberToTeam(scienceTeam.id, sattler.id);
351
352// 3. Raport o zespołach
353staffManagementSystem.generateTeamsReport();
354
355// 4. Reorganizacja zespołów
356console.log("\n=== REORGANIZACJA ZESPOŁÓW ===");
357
358// Tworzenie nowego zespołu zarządzania kryzysowego
359const crisisTeam = staffManagementSystem.createTeam("Zespół Zarządzania Kryzysowego", "Reagowanie na sytuacje kryzysowe", claire.id);
360
361// Przenoszenie członków z innych zespołów
362staffManagementSystem.reorganizeTeams(securityTeam.id, crisisTeam.id, [vivian.id]);
363staffManagementSystem.addMemberToTeam(crisisTeam.id, malcolm.id); // Dodajemy Malcolma jako doradcę ds. ryzyka
364staffManagementSystem.addMemberToTeam(crisisTeam.id, harding.id);  // Dodajemy weterynarza
365
366// Zmiana lidera zespołu naukowego
367staffManagementSystem.changeTeamLeader(scienceTeam.id, sattler.id);
368
369// Dezaktywacja jednego zespołu
370staffManagementSystem.deactivateTeam(raptorTeam.id, "Projekt zawieszony - incydent z raptorami");
371
372// 5. Finalny raport o zespołach (z uwzględnieniem nieaktywnych)
373staffManagementSystem.generateTeamsReport(true);

Podsumowanie

W tej lekcji poznaliśmy podstawowe metody modyfikacji tablic w JavaScript:

  1. push() - dodaje elementy na końcu tablicy
  2. pop() - usuwa i zwraca ostatni element tablicy
  3. unshift() - dodaje elementy na początku tablicy
  4. shift() - usuwa i zwraca pierwszy element tablicy
  5. splice() - dodaje, usuwa lub zastępuje elementy w dowolnym miejscu tablicy
  6. sort() - sortuje elementy tablicy
  7. reverse() - odwraca kolejność elementów w tablicy
  8. fill() - wypełnia elementy tablicy określoną wartością

Umiejętność efektywnego wykorzystania tych metod jest kluczowa przy pracy z danymi w JavaScript. Pozwalają one na zarządzanie kolekcjami, implementowanie struktur danych (takich jak stosy i kolejki) oraz efektywną manipulację danymi.

Warto pamiętać, że powyższe metody modyfikują oryginalną tablicę (z wyjątkiem przypadków, gdy tworzymy kopię przed ich zastosowaniem). W jednej z kolejnych lekcji poznamy metody tablic, które nie modyfikują oryginału, ale zwracają nową tablicę lub wartość.

Kod do tej lekcji: index.js
1// Metody tablic - Park Jurajski
2console.log("Laboratorium Metod Tablic");
3console.log("Zaawansowane operacje na kolekcjach\n");
4
5// ===========================================
6// 1. forEach() - ITERACJA
7// ===========================================
8console.log("=== 1. forEach() - Iteracja ===");
9
10const dinosaurs = ["T-Rex", "Velociraptor", "Triceratops", "Stegosaurus"];
11
12console.log("\nLista dinozaurów:");
13dinosaurs.forEach((dino, index) => {
14  console.log(`  ${index + 1}. ${dino}`);
15});
16
17// forEach z pełnymi parametrami
18console.log("\nPełna wersja forEach:");
19dinosaurs.forEach((element, index, array) => {
20  console.log(`  [${index}] ${element} z tablicy długości ${array.length}`);
21});
22
23// ===========================================
24// 2. map() - TRANSFORMACJA
25// ===========================================
26console.log("\n=== 2. map() - Transformacja ===");
27
28const dinoData = [
29  { name: "T-Rex", weight: 8000 },
30  { name: "Velociraptor", weight: 80 },
31  { name: "Triceratops", weight: 9000 }
32];
33
34// Wyciągnięcie samych nazw
35const names = dinoData.map(dino => dino.name);
36console.log("\nSame nazwy:", names);
37
38// Transformacja do nowego formatu
39const formatted = dinoData.map(dino => ({
40  species: dino.name,
41  weightInTons: dino.weight / 1000,
42  category: dino.weight > 1000 ? "Duży" : "Mały"
43}));
44
45console.log("\nPrzetransformowane:");
46formatted.forEach(d => {
47  console.log(`  ${d.species}: ${d.weightInTons}t - ${d.category}`);
48});
49
50// ===========================================
51// 3. filter() - FILTROWANIE
52// ===========================================
53console.log("\n=== 3. filter() - Filtrowanie ===");
54
55const allDinos = [
56  { name: "T-Rex", weight: 8000, carnivore: true },
57  { name: "Velociraptor", weight: 80, carnivore: true },
58  { name: "Triceratops", weight: 9000, carnivore: false },
59  { name: "Stegosaurus", weight: 5000, carnivore: false },
60  { name: "Brachiosaurus", weight: 50000, carnivore: false }
61];
62
63// Tylko mięsożercy
64const carnivores = allDinos.filter(dino => dino.carnivore);
65console.log("\nMięsożercy:", carnivores.map(d => d.name));
66
67// Ciężkie dinozaury (> 5000kg)
68const heavy = allDinos.filter(dino => dino.weight > 5000);
69console.log("Ciężkie (>5t):", heavy.map(d => d.name));
70
71// Łączenie warunków
72const heavyCarnivores = allDinos.filter(d => d.carnivore && d.weight > 100);
73console.log("Ciężkie mięsożercy:", heavyCarnivores.map(d => d.name));
74
75// ===========================================
76// 4. find() i findIndex()
77// ===========================================
78console.log("\n=== 4. find() i findIndex() ===");
79
80const park = [
81  { id: 1, name: "T-Rex", sector: "A" },
82  { id: 2, name: "Velociraptor", sector: "B" },
83  { id: 3, name: "Triceratops", sector: "C" }
84];
85
86// find() - pierwszy element spełniający warunek
87const foundDino = park.find(dino => dino.sector === "B");
88console.log("\nfind(sector === 'B'):", foundDino);
89
90// findIndex() - indeks pierwszego elementu
91const foundIndex = park.findIndex(dino => dino.name === "Triceratops");
92console.log("findIndex(name === 'Triceratops'):", foundIndex);
93
94// Nieznaleziony element
95const notFound = park.find(dino => dino.name === "Pteranodon");
96console.log("find(Pteranodon):", notFound); // undefined
97
98// ===========================================
99// 5. reduce() - AGREGACJA
100// ===========================================
101console.log("\n=== 5. reduce() - Agregacja ===");
102
103const weights = [
104  { name: "T-Rex", weight: 8000 },
105  { name: "Velociraptor", weight: 80 },
106  { name: "Triceratops", weight: 9000 },
107  { name: "Stegosaurus", weight: 5000 }
108];
109
110// Suma wag
111const totalWeight = weights.reduce((sum, dino) => sum + dino.weight, 0);
112console.log(`\nŁączna waga: ${totalWeight}kg`);
113
114// Średnia waga
115const avgWeight = totalWeight / weights.length;
116console.log(`Średnia waga: ${avgWeight.toFixed(2)}kg`);
117
118// Grupowanie po wadze
119const grouped = weights.reduce((acc, dino) => {
120  const category = dino.weight > 1000 ? "heavy" : "light";
121  if (!acc[category]) acc[category] = [];
122  acc[category].push(dino.name);
123  return acc;
124}, {});
125
126console.log("\nPogrupowane:");
127console.log("  Ciężkie:", grouped.heavy);
128console.log("  Lekkie:", grouped.light);
129
130// Znajdowanie max
131const heaviest = weights.reduce((max, dino) =>
132  dino.weight > max.weight ? dino : max
133);
134console.log(`\nNajcięższy: ${heaviest.name} (${heaviest.weight}kg)`);
135
136// ===========================================
137// 6. some() i every()
138// ===========================================
139console.log("\n=== 6. some() i every() ===");
140
141const herd = [
142  { name: "Trike", health: 95 },
143  { name: "Stego", health: 88 },
144  { name: "Brachio", health: 72 }
145];
146
147// some() - czy przynajmniej jeden
148const hasSick = herd.some(dino => dino.health < 80);
149console.log("\nCzy są chore (<80%):", hasSick);
150
151const hasHealthy = herd.some(dino => dino.health > 90);
152console.log("Czy są bardzo zdrowe (>90%):", hasHealthy);
153
154// every() - czy wszystkie
155const allHealthy = herd.every(dino => dino.health >= 80);
156console.log("Czy wszystkie zdrowe (≥80%):", allHealthy);
157
158const allCritical = herd.every(dino => dino.health < 50);
159console.log("Czy wszystkie krytyczne (<50%):", allCritical);
160
161// ===========================================
162// 7. sort() - SORTOWANIE
163// ===========================================
164console.log("\n=== 7. sort() - Sortowanie ===");
165
166const unsorted = ["Velociraptor", "T-Rex", "Brachiosaurus", "Stegosaurus"];
167
168// Alfabetycznie
169const alphabetical = [...unsorted].sort();
170console.log("\nAlfabetycznie:", alphabetical);
171
172// Odwrotnie alfabetycznie
173const reverseAlpha = [...unsorted].sort((a, b) => b.localeCompare(a));
174console.log("Odwrotnie:", reverseAlpha);
175
176// Sortowanie obiektów
177const dinoStats = [
178  { name: "T-Rex", health: 85 },
179  { name: "Velociraptor", health: 100 },
180  { name: "Triceratops", health: 70 }
181];
182
183const byHealth = [...dinoStats].sort((a, b) => b.health - a.health);
184console.log("\nPo zdrowiu (malejąco):");
185byHealth.forEach(d => console.log(`  ${d.name}: ${d.health}%`));
186
187// ===========================================
188// 8. ŁAŃCUCHOWANIE METOD
189// ===========================================
190console.log("\n=== 8. Łańcuchowanie metod ===");
191
192const inventory = [
193  { name: "Rexy", species: "T-Rex", sector: "A", health: 95, carnivore: true },
194  { name: "Blue", species: "Velociraptor", sector: "B", health: 100, carnivore: true },
195  { name: "Charlie", species: "Velociraptor", sector: "B", health: 85, carnivore: true },
196  { name: "Trike", species: "Triceratops", sector: "C", health: 70, carnivore: false },
197  { name: "Stego", species: "Stegosaurus", sector: "C", health: 92, carnivore: false }
198];
199
200// Znajdź zdrowych mięsożerców i posortuj po nazwie
201const healthyCarnivores = inventory
202  .filter(d => d.carnivore)
203  .filter(d => d.health >= 90)
204  .map(d => d.name)
205  .sort();
206
207console.log("\nZdrowi mięsożercy (≥90%):", healthyCarnivores);
208
209// Suma zdrowia roślinożerców
210const herbivoreHealth = inventory
211  .filter(d => !d.carnivore)
212  .reduce((sum, d) => sum + d.health, 0);
213
214console.log(`Suma zdrowia roślinożerców: ${herbivoreHealth}%`);
215
216// ===========================================
217// 9. flat() i flatMap()
218// ===========================================
219console.log("\n=== 9. flat() i flatMap() ===");
220
221const sectors = [
222  ["T-Rex", "Spinosaurus"],
223  ["Velociraptor", "Dilophosaurus"],
224  ["Triceratops", "Stegosaurus"]
225];
226
227// flat() - spłaszczenie zagnieżdżonej tablicy
228const flatDinos = sectors.flat();
229console.log("\nflat():", flatDinos);
230
231// flatMap() - map + flat w jednym
232const withEmoji = sectors.flatMap(sector =>
233  sector.map(dino => `${dino}`)
234);
235console.log("flatMap():", withEmoji);
236
237// ===========================================
238// 10. PRZYKŁAD KOMPLEKSOWY
239// ===========================================
240console.log("\n=== 10. System analizy - przykład kompleksowy ===");
241
242class DinosaurAnalytics {
243  constructor(dinosaurs) {
244    this.dinosaurs = dinosaurs;
245  }
246
247  // Statystyki ogólne
248  getOverallStats() {
249    const total = this.dinosaurs.length;
250    const avgHealth = this.dinosaurs.reduce((sum, d) => sum + d.health, 0) / total;
251    const carnivoreCount = this.dinosaurs.filter(d => d.carnivore).length;
252
253    return {
254      total,
255      avgHealth: avgHealth.toFixed(1),
256      carnivores: carnivoreCount,
257      herbivores: total - carnivoreCount
258    };
259  }
260
261  // Top N po zdrowiu
262  getHealthiest(n = 3) {
263    return this.dinosaurs
264      .sort((a, b) => b.health - a.health)
265      .slice(0, n)
266      .map(d => ({ name: d.name, health: d.health }));
267  }
268
269  // Grupowanie po sektorze
270  groupBySector() {
271    return this.dinosaurs.reduce((acc, dino) => {
272      if (!acc[dino.sector]) acc[dino.sector] = [];
273      acc[dino.sector].push(dino.name);
274      return acc;
275    }, {});
276  }
277
278  // Alerty dla chorych
279  getHealthAlerts() {
280    return this.dinosaurs
281      .filter(d => d.health < 80)
282      .map(d => ({
283        name: d.name,
284        sector: d.sector,
285        health: d.health,
286        priority: d.health < 50 ? "KRYTYCZNY" : "UWAGA"
287      }))
288      .sort((a, b) => a.health - b.health);
289  }
290
291  // Raport sektora
292  getSectorReport(sector) {
293    const dinosInSector = this.dinosaurs.filter(d => d.sector === sector);
294
295    if (dinosInSector.length === 0) {
296      return { error: `Brak dinozaurów w sektorze ${sector}` };
297    }
298
299    const avgHealth = dinosInSector.reduce((sum, d) => sum + d.health, 0) / dinosInSector.length;
300    const carnivores = dinosInSector.filter(d => d.carnivore);
301    const herbivores = dinosInSector.filter(d => !d.carnivore);
302
303    return {
304      sector,
305      total: dinosInSector.length,
306      avgHealth: avgHealth.toFixed(1),
307      carnivores: carnivores.map(d => d.name),
308      herbivores: herbivores.map(d => d.name),
309      list: dinosInSector.map(d => `${d.name} (${d.health}%)`)
310    };
311  }
312
313  // Wyświetl wszystkie analizy
314  displayFullReport() {
315    console.log("\nPEŁNY RAPORT ANALITYCZNY");
316    console.log("=".repeat(70));
317
318    // Statystyki ogólne
319    const stats = this.getOverallStats();
320    console.log("\nStatystyki ogólne:");
321    console.log(`   Łącznie dinozaurów: ${stats.total}`);
322    console.log(`   Średnie zdrowie: ${stats.avgHealth}%`);
323    console.log(`   Mięsożercy: ${stats.carnivores}`);
324    console.log(`   Roślinożercy: ${stats.herbivores}`);
325
326    // Najzdrowsze
327    const healthiest = this.getHealthiest(3);
328    console.log("\nTOP 3 - Najzdrowsze:");
329    healthiest.forEach((d, i) => {
330      console.log(`   ${i + 1}. ${d.name} - ${d.health}%`);
331    });
332
333    // Podział na sektory
334    const sectors = this.groupBySector();
335    console.log("\nPodział na sektory:");
336    Object.entries(sectors).sort().forEach(([sector, dinos]) => {
337      console.log(`   Sektor ${sector}: ${dinos.join(", ")}`);
338    });
339
340    // Alerty zdrowotne
341    const alerts = this.getHealthAlerts();
342    if (alerts.length > 0) {
343      console.log("\nALERTY ZDROWOTNE:");
344      alerts.forEach(alert => {
345        console.log(`   [${alert.priority}] ${alert.name} - Sektor ${alert.sector} - ${alert.health}%`);
346      });
347    } else {
348      console.log("\nBrak alertów zdrowotnych");
349    }
350
351    // Raporty sektorów
352    const uniqueSectors = [...new Set(this.dinosaurs.map(d => d.sector))].sort();
353    console.log("\nSZCZEGÓŁY SEKTORÓW:");
354    uniqueSectors.forEach(sector => {
355      const report = this.getSectorReport(sector);
356      console.log(`\n   Sektor ${report.sector}:`);
357      console.log(`     Dinozaury: ${report.total}`);
358      console.log(`     Średnie zdrowie: ${report.avgHealth}%`);
359      if (report.carnivores.length > 0) {
360        console.log(`     Mięsożercy: ${report.carnivores.join(", ")}`);
361      }
362      if (report.herbivores.length > 0) {
363        console.log(`     Roślinożercy: ${report.herbivores.join(", ")}`);
364      }
365    });
366  }
367}
368
369// Dane testowe
370const testData = [
371  { name: "Rexy", species: "T-Rex", sector: "A", health: 95, carnivore: true },
372  { name: "Blue", species: "Velociraptor", sector: "B", health: 100, carnivore: true },
373  { name: "Charlie", species: "Velociraptor", sector: "B", health: 85, carnivore: true },
374  { name: "Delta", species: "Velociraptor", sector: "B", health: 45, carnivore: true },
375  { name: "Trike", species: "Triceratops", sector: "C", health: 70, carnivore: false },
376  { name: "Stego", species: "Stegosaurus", sector: "C", health: 92, carnivore: false },
377  { name: "Brachio", species: "Brachiosaurus", sector: "D", health: 88, carnivore: false }
378];
379
380// Uruchom analizę
381const analytics = new DinosaurAnalytics(testData);
382analytics.displayFullReport();
383
384console.log("\nGratulacje! Opanowałeś metody tablic!");

Sprawdź się

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

  1. 1. Co robi metoda Array.prototype.pop()?

  2. 2. Co zwróci typeof undefined?

  3. 3. Który sposób jest najprostszy do sprawdzenia wartości zmiennej podczas debugowania?

  4. 4. Dlaczego warto używać const zamiast let, gdy wartość zmiennej nie będzie zmieniana?

Zadania praktyczne w grze

  • Edytor kodu

    Utwórz tablicę species z gatunkami 'T-Rex', 'Velociraptor' i 'Triceratops'. Metodą push() dodaj na koniec 'Stegosaurus', a metodą shift() usuń pierwszy element i zapisz go w stałej removed. Wyświetl w konsoli tablicę species i jej długość (length).

  • Klikanie w kolejności

    Ułóż elementy template literal z interpolacją:

  • Edytor kodu

    Zapisz jako BigInt: const dinoEraStart = 245000000n (początek ery dinozaurów w latach temu) i const dinoEraEnd = 66000000n (jej koniec). Oblicz dinoEraDuration jako różnicę dinoEraStart - dinoEraEnd, a dinoEraDays jako dinoEraDuration pomnożone przez 365n. Wyświetl oba wyniki w konsoli.

  • Układanie w poziomie

    Ułóż elementy sprawdzania typu zmiennej:

  • Edytor kodu

    Utwórz stałą species z nazwą gatunku dinozaura zapisaną jako tekst, np. 'Tyrannosaurus Rex', i zmienną age (let) z wiekiem 25. Po urodzinach zmień wartość age na 26 i wyświetl w konsoli species i age.

  • Układanie w pionie

    Uporządkuj miejsca, w których JavaScript szuka zmiennej (scope chain):

  • Układanie w poziomie

    Ułóż elementy deklaracji funkcji strzałkowej (arrow function):

  • Edytor kodu

    Napisz funkcję ageInMillions(years), która zwraca wiek skamieniałości w milionach lat, zaokrąglony do liczby całkowitej przez Math.round(). Wywołaj ją dla skamieniałości sprzed 66000000 lat i wyświetl wynik w konsoli.

  • Klikanie w kolejności

    Uporządkuj proces alokacji pamięci dla zmiennych:

  • Układanie w pionie

    Uporządkuj kroki sprawdzania typu zmiennej w kodzie produkcyjnym:

  • Układanie w pionie

    Uporządkuj wprowadzenie nowych typów danych w JavaScript chronologicznie:

  • Klikanie w kolejności

    Ułóż elementy deklaracji tablicy w JavaScript

  • Układanie w pionie

    Uporządkuj etapy zarządzania pamięcią dla zmiennych w JavaScript:

  • Układanie w poziomie

    Ułóż elementy dodawania elementu do tablicy:

Przydatne artykuły