JavaScript and TypeScript course Β· Module 2: Functions, Arrays and Objects
Array Iteration (forEach, map, filter, reduce)
In this lesson9
In our Jurassic Park we collect a mountain of data about the dinosaurs - their species, measurements, feeding habits, aggression levels and much more. Once all of that lands in an array, we hardly ever care about a single entry: we want to visit every element, reshape them, pull out the ones that matter, or boil the whole herd down to one number for the control room. JavaScript ships a family of array iteration methods built for exactly that, and each of them has a job of its own.
The forEach Method
The forEach method runs a function once for every element of an array. It is the most basic way of iterating - we reach for it when we want to do something with each element, such as printing it or sending it to the monitoring station, and we do not need a new array in return.
1// An array of objects representing the dinosaurs in our park
2const dinosaurs = [
3 { name: "Tyrannosaurus", diet: "carnivore", aggressionLevel: 10 },
4 { name: "Triceratops", diet: "herbivore", aggressionLevel: 4 },
5 { name: "Velociraptor", diet: "carnivore", aggressionLevel: 9 },
6 { name: "Stegosaurus", diet: "herbivore", aggressionLevel: 2 },
7 { name: "Brachiosaurus", diet: "herbivore", aggressionLevel: 1 }
8];
9
10// We use forEach to print the names of all the dinosaurs
11console.log("List of dinosaurs in the park:");
12dinosaurs.forEach(function(dino) {
13 console.log(dino.name);
14});
15
16// We can also use an arrow function for a more concise notation
17console.log("List of dinosaurs with their aggression level:");
18dinosaurs.forEach(dino => {
19 console.log(`${dino.name}: Aggression level ${dino.aggressionLevel}/10`);
20});The forEach method hands nothing back - to be exact, it returns undefined - so we use it when we only want to perform an action on the elements, without building a new array. It is worth knowing what it cannot do either: you cannot stop it halfway through. A return inside the callback ends that one call and the walk moves straight on to the next dinosaur, and break is not even allowed in there. When you need to bail out early, that is a job for find, some, every or a classic for loop.
The map Method
The map method creates a new array containing the results of calling a provided function on every element of the original array. It is remarkably useful whenever we need to convert data from one shape into another - turning full dinosaur records into a plain list of names, or into short safety summaries the wardens can actually read.
1// We use map to build a new array containing only the dinosaur names
2const dinoNames = dinosaurs.map(dino => dino.name);
3console.log("Dinosaur names:", dinoNames);
4// Result: ["Tyrannosaurus", "Triceratops", "Velociraptor", "Stegosaurus", "Brachiosaurus"]
5
6// We can also build more complex transformations
7const dinoSummaries = dinosaurs.map(dino => {
8 return {
9 name: dino.name,
10 dangerLevel: dino.diet === "carnivore" ? "High" : "Low",
11 warning: dino.aggressionLevel > 7 ? "Handle with extreme caution!" : "Standard safety procedures"
12 };
13});
14
15console.log("Dinosaur safety summary:", dinoSummaries);Unlike forEach, the map method always returns a new array of the same length as the original, holding the transformed elements. That is exactly the key difference between the two: map() returns a new array, while forEach() returns undefined. It is not a question of speed - map() is not faster, both methods visit every element exactly once - and they certainly do not work identically. Neither of them can be stopped mid-run, so that is no difference between them either. Reach for map when you want the result, and for forEach when you only care about the side effect.
The filter Method
The filter method creates a new array containing only the elements that satisfy a given condition. It is the perfect tool when we need to pull a subset out of a bigger collection - every carnivore, every animal due for a health check, every enclosure in the eastern sector.
1// We filter to get only the carnivorous dinosaurs
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Carnivorous dinosaurs:", carnivores);
4
5// We can combine conditions to build more complex filters
6const dangerousDinos = dinosaurs.filter(dino => {
7 return dino.diet === "carnivore" && dino.aggressionLevel > 7;
8});
9console.log("The most dangerous dinosaurs:", dangerousDinos);
10
11// We can also combine filter with map, to narrow down first and transform afterwards
12const safetyWarnings = dinosaurs
13 .filter(dino => dino.aggressionLevel > 5)
14 .map(dino => `WARNING: ${dino.name} has a high aggression level!`);
15
16console.log("Safety warnings:", safetyWarnings);The filter method returns a new array which may be shorter than the original, holding only the elements that made it through the "filter" - the ones for which the test function returned true. Notice that the dinosaurs array itself is left completely untouched: just like map, filter never edits the collection it was called on, it only reports on it.
The reduce Method
The reduce method is the most powerful of the iteration methods, and also the most involved. It lets us "reduce" an array to a single value by running a function over every element while carrying the result of the previous step along with us.
1// We calculate the sum of the aggression levels of all the dinosaurs
2const totalAggressionLevel = dinosaurs.reduce((total, dino) => {
3 return total + dino.aggressionLevel;
4}, 0); // 0 is the initial value
5
6console.log("Total aggression level in the park:", totalAggressionLevel);
7
8// We can use reduce to build richer structures, such as an object grouping dinosaurs by diet
9const dinosByDiet = dinosaurs.reduce((groups, dino) => {
10 // If the group for this diet does not exist yet, we create it
11 if (!groups[dino.diet]) {
12 groups[dino.diet] = [];
13 }
14
15 // We add the dinosaur to the matching group
16 groups[dino.diet].push(dino.name);
17
18 return groups;
19}, {}); // {} is the initial value (an empty object)
20
21console.log("Dinosaurs grouped by diet:", dinosByDiet);
22// Result: { "carnivore": ["Tyrannosaurus", "Velociraptor"], "herbivore": ["Triceratops", "Stegosaurus", "Brachiosaurus"] }So what is the reduce() method on an array used for? It reduces an array to a single value - a sum, an average, the heaviest specimen, or one grouped object like the one above. The name misleads a lot of people, so let us be precise about what it does not do: it does not remove elements from an array, it does not reduce the size of the array (our dinosaurs array still holds five records afterwards), and it has nothing to do with sorting in descending order - that job belongs to sort.
The reduce method takes two parameters:
- A reducer function, which itself takes at least two parameters:
- The accumulator: the value returned from the previous iteration (or the initial value)
- The current element: the element of the array being processed right now
- An initial value (optional, but strongly recommended)
The find Method
The find method returns the first element of an array that satisfies a given condition. When the park systems need one specific record rather than a whole list, this is the method to reach for.
1// We look for a dinosaur with a given name
2const stegosaurus = dinosaurs.find(dino => dino.name === "Stegosaurus");
3console.log("Stegosaurus information:", stegosaurus);
4
5// We look for the first carnivore with an aggression level above 8
6const dangerousCarnivore = dinosaurs.find(dino => {
7 return dino.diet === "carnivore" && dino.aggressionLevel > 8;
8});
9console.log("First dangerous carnivore:", dangerousCarnivore); // TyrannosaurusUnlike filter, which returns an array of every matching element, find returns only the first match, and when nothing matches it returns undefined. It also stops the moment it gets a hit, so the remaining dinosaurs are never examined. Its close twin findIndex behaves the same way but gives you the position of that first match instead of the element itself, and returns -1 when there is no match at all.
The some and every Methods
The some and every methods return a boolean value and are useful for checking whether the elements of an array meet certain criteria. Neither of them builds a new array - they answer a yes-or-no question about the whole collection.
some
The some method checks whether at least one element of the array satisfies the given condition.
1// We check whether there are any dangerous dinosaurs in the park
2const hasHighAggressionDinos = dinosaurs.some(dino => dino.aggressionLevel > 8);
3console.log("Does the park have high aggression dinosaurs?", hasHighAggressionDinos); // true
4
5// We check whether there are any omnivorous dinosaurs in the park
6const hasOmnivores = dinosaurs.some(dino => dino.diet === "omnivore");
7console.log("Does the park have omnivorous dinosaurs?", hasOmnivores); // falseThe some method short-circuits: it stops as soon as it finds the first element that passes the test, because it already knows the answer is true and looking at the rest of the herd would change nothing.
every
The every method checks whether all the elements of the array satisfy the given condition.
1// We check whether every dinosaur has an aggression level assigned
2const allHaveAggressionLevel = dinosaurs.every(dino => dino.aggressionLevel !== undefined);
3console.log("Do all the dinosaurs have an aggression level?", allHaveAggressionLevel); // true
4
5// We check whether all the dinosaurs are herbivores
6const allHerbivores = dinosaurs.every(dino => dino.diet === "herbivore");
7console.log("Are all the dinosaurs herbivores?", allHerbivores); // falseThe every method short-circuits the other way round: it stops at the first element that fails the test, because a single counterexample is enough to answer false. Read out loud, the pair sounds almost like plain English - "is there some dangerous dinosaur?" and "is every dinosaur fed?".
The sort Method
The sort method orders the elements of an array and returns the sorted array. By default it compares elements as strings, which can lead to unexpected results when sorting numbers - by default [10, 9, 100] comes out as [10, 100, 9]. That is why we usually pass it a comparison function.
1// We sort the dinosaurs by name (alphabetically)
2const dinosSortedByName = [...dinosaurs].sort((a, b) => {
3 return a.name.localeCompare(b.name);
4});
5console.log("Dinosaurs sorted alphabetically:", dinosSortedByName);
6
7// We sort the dinosaurs by aggression level (from the least to the most aggressive)
8const dinosSortedByAggression = [...dinosaurs].sort((a, b) => {
9 return a.aggressionLevel - b.aggressionLevel;
10});
11console.log("Dinosaurs sorted by aggression (ascending):", dinosSortedByAggression);
12
13// We sort the dinosaurs by aggression level (from the most to the least aggressive)
14const mostDangerousFirst = [...dinosaurs].sort((a, b) => {
15 return b.aggressionLevel - a.aggressionLevel;
16});
17console.log("Dinosaurs sorted by aggression (descending):", mostDangerousFirst);Notice that we use the spread operator (...) to create a copy of the array before sorting, because sort modifies the original array and we want to preserve the original order. The comparison function decides the direction: a negative result puts a first, which is why a.aggressionLevel - b.aggressionLevel sorts ascending, and flipping the two operands sorts descending.
Combining Methods (Chaining)
One of the most powerful features of these methods is that we can join them into "chains" and carry out complex operations on our data in a single readable expression. It works because filter, map and sort all give back an array, so the next method can be called straight on the result.
1// Let us find the names of all the herbivorous dinosaurs, sorted alphabetically
2const sortedHerbivoreNames = dinosaurs
3 .filter(dino => dino.diet === "herbivore") // First we filter
4 .map(dino => dino.name) // Then we transform into an array of names
5 .sort(); // Finally we sort alphabetically
6
7console.log("Herbivores (alphabetically):", sortedHerbivoreNames);
8// Result: ["Brachiosaurus", "Stegosaurus", "Triceratops"]
9
10// Let us calculate the average aggression level of the carnivores
11const averageCarnivoreAggression = dinosaurs
12 .filter(dino => dino.diet === "carnivore") // First we keep only the carnivores
13 .reduce((sum, dino, index, array) => { // Then we reduce it down to an average
14 sum += dino.aggressionLevel; // We add the aggression level to the sum
15
16 // If this is the last element, we divide by the number of elements
17 if (index === array.length - 1) {
18 return sum / array.length;
19 }
20
21 return sum; // We return the partial sum
22 }, 0);
23
24console.log("Average carnivore aggression level:", averageCarnivoreAggression); // 9.5Read a chain from left to right and the order of its links is always the same: the array comes first, then the method that narrows it down, then the method that transforms whatever survived. The array is the receiver, so dinosaurs always opens the chain, then comes .filter(...), and only then .map(...).
1// If every record carried a ready dangerous flag, the chain would read like a sentence
2const dangerousNames = dinosaurs
3 .filter(d => d.dangerous)
4 .map(d => d.name);
5
6// Our records store a diet string instead, so the test is a little longer -
7// but the order of the links in the chain never changes
8const carnivoreNames = dinosaurs
9 .filter(d => d.diet === "carnivore")
10 .map(d => d.name);
11
12console.log("Carnivore names:", carnivoreNames);Swapping those two links around would break everything: after .map(d => d.name) we are holding an array of plain strings, and a string has no dangerous property left to filter on. Filter first, map second - that is the order worth memorising.
Summary
The array iteration methods in JavaScript are an invaluable tool when working with collections of data. Each of them has its own speciality:
- forEach: when we want to perform an operation on every element, without creating a new array
- map: when we want to transform every element of the array into something else
- filter: when we want to select the subgroup of elements that meet given criteria
- reduce: when we want to "reduce" the array to a single value or a new structure
- find: when we are looking for the first element that meets a given criterion
- some/every: when we want to check whether some/all elements meet a given criterion
- sort: when we want to put the elements of the array in order
In our Jurassic Park these methods are indispensable for managing dinosaur data efficiently, monitoring safety and making decisions based on data. As Dr. Rex puts it, iteration methods are the park's morning briefing: forEach walks the daily checklist, filter picks out only the animals that need attention, map turns them into a report, and reduce gives the control room a single number to act on.
Code for this lesson: index.js
1// Iterating over arrays - Jurassic Park
2console.log("Advanced Iterations");
3console.log("forEach, map, filter, reduce\n");
4
5// ===========================================
6// TEST DATA
7// ===========================================
8const dinosaurs = [
9 { id: 1, name: "Rexy", species: "T-Rex", age: 8, weight: 8000, sector: "A", health: 95, carnivore: true, fed: true },
10 { id: 2, name: "Blue", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 100, carnivore: true, fed: false },
11 { id: 3, name: "Charlie", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 85, carnivore: true, fed: false },
12 { id: 4, name: "Delta", species: "Velociraptor", age: 5, weight: 80, sector: "B", health: 45, carnivore: true, fed: true },
13 { id: 5, name: "Trike", species: "Triceratops", age: 7, weight: 9000, sector: "C", health: 70, carnivore: false, fed: true },
14 { id: 6, name: "Stego", species: "Stegosaurus", age: 6, weight: 5000, sector: "C", health: 92, carnivore: false, fed: false },
15 { id: 7, name: "Brachio", species: "Brachiosaurus", age: 10, weight: 50000, sector: "D", health: 88, carnivore: false, fed: true }
16];
17
18// ===========================================
19// 1. forEach() - CLASSIC ITERATION
20// ===========================================
21console.log("=== 1. forEach() - Iteration without a return value ===");
22
23console.log("\nList of all dinosaurs:");
24dinosaurs.forEach((dino, index) => {
25 const icon = dino.carnivore ? "carnivore" : "herbivore";
26 console.log(` ${index + 1}. ${icon} ${dino.name} (${dino.species})`);
27});
28
29// forEach with side effects
30let totalWeight = 0;
31dinosaurs.forEach(dino => {
32 totalWeight += dino.weight;
33});
34console.log(`\nTotal weight: ${totalWeight}kg`);
35
36// ===========================================
37// 2. map() - DATA TRANSFORMATION
38// ===========================================
39console.log("\n=== 2. map() - Transformation of each element ===");
40
41// Example 1: Simple value extraction
42const names = dinosaurs.map(dino => dino.name);
43console.log("\nNames only:", names.join(", "));
44
45// Example 2: Creating new objects
46const summaries = dinosaurs.map(dino => ({
47 name: dino.name,
48 type: dino.carnivore ? "Carnivore" : "Herbivore",
49 status: dino.health >= 80 ? "Healthy" : "Needs attention"
50}));
51
52console.log("\nSummaries:");
53summaries.forEach(s => {
54 console.log(` ${s.name}: ${s.type}, Status: ${s.status}`);
55});
56
57// Example 3: Calculations
58const healthPercentages = dinosaurs.map(dino => {
59 const healthStatus = dino.health >= 90 ? "*" : dino.health >= 70 ? "OK" : "!";
60 return `${healthStatus} ${dino.name}: ${dino.health}%`;
61});
62
63console.log("\nHealth levels:");
64healthPercentages.forEach(h => console.log(` ${h}`));
65
66// ===========================================
67// 3. filter() - FILTERING
68// ===========================================
69console.log("\n=== 3. filter() - Selecting elements ===");
70
71// Example 1: One condition
72const carnivores = dinosaurs.filter(dino => dino.carnivore);
73console.log(`\nCarnivores (${carnivores.length}):`, carnivores.map(d => d.name).join(", "));
74
75// Example 2: Multiple conditions
76const healthyCarnivores = dinosaurs.filter(dino =>
77 dino.carnivore && dino.health >= 80
78);
79console.log(`Healthy carnivores (${healthyCarnivores.length}):`, healthyCarnivores.map(d => d.name).join(", "));
80
81// Example 3: Complex conditions
82const needsAttention = dinosaurs.filter(dino =>
83 dino.health < 80 || !dino.fed
84);
85console.log(`\nNeed attention (${needsAttention.length}):`);
86needsAttention.forEach(dino => {
87 const reasons = [];
88 if (dino.health < 80) reasons.push(`health: ${dino.health}%`);
89 if (!dino.fed) reasons.push("hungry");
90 console.log(` ${dino.name}: ${reasons.join(", ")}`);
91});
92
93// Example 4: Filtering by sector
94const sectorB = dinosaurs.filter(dino => dino.sector === "B");
95console.log(`\nSector B (${sectorB.length}):`, sectorB.map(d => d.name).join(", "));
96
97// ===========================================
98// 4. reduce() - AGGREGATION
99// ===========================================
100console.log("\n=== 4. reduce() - Data aggregation ===");
101
102// Example 1: Sum
103const totalHealthPoints = dinosaurs.reduce((sum, dino) => sum + dino.health, 0);
104console.log(`\nSum of health points: ${totalHealthPoints}`);
105
106// Example 2: Average
107const avgHealth = totalHealthPoints / dinosaurs.length;
108console.log(`Average health: ${avgHealth.toFixed(1)}%`);
109
110// Example 3: Finding max/min
111const oldest = dinosaurs.reduce((max, dino) =>
112 dino.age > max.age ? dino : max
113);
114console.log(`\nOldest: ${oldest.name} (${oldest.age} years)`);
115
116const youngest = dinosaurs.reduce((min, dino) =>
117 dino.age < min.age ? dino : min
118);
119console.log(`Youngest: ${youngest.name} (${youngest.age} years)`);
120
121// Example 4: Grouping
122const bySector = dinosaurs.reduce((acc, dino) => {
123 if (!acc[dino.sector]) acc[dino.sector] = [];
124 acc[dino.sector].push(dino.name);
125 return acc;
126}, {});
127
128console.log("\nGrouping by sector:");
129Object.entries(bySector).sort().forEach(([sector, dinos]) => {
130 console.log(` Sector ${sector}: ${dinos.join(", ")}`);
131});
132
133// Example 5: Counting occurrences
134const speciesCount = dinosaurs.reduce((acc, dino) => {
135 acc[dino.species] = (acc[dino.species] || 0) + 1;
136 return acc;
137}, {});
138
139console.log("\nCount by species:");
140Object.entries(speciesCount).forEach(([species, count]) => {
141 console.log(` ${species}: ${count}`);
142});
143
144// Example 6: Building a statistics object
145const stats = dinosaurs.reduce((acc, dino) => {
146 acc.totalDinos++;
147 acc.totalWeight += dino.weight;
148 acc.totalAge += dino.age;
149 if (dino.carnivore) acc.carnivores++;
150 if (dino.health >= 90) acc.veryHealthy++;
151 return acc;
152}, { totalDinos: 0, totalWeight: 0, totalAge: 0, carnivores: 0, veryHealthy: 0 });
153
154console.log("\nSummary statistics:");
155console.log(` Total dinosaurs: ${stats.totalDinos}`);
156console.log(` Total weight: ${stats.totalWeight}kg`);
157console.log(` Average age: ${(stats.totalAge / stats.totalDinos).toFixed(1)} years`);
158console.log(` Carnivores: ${stats.carnivores}`);
159console.log(` Very healthy (β₯90%): ${stats.veryHealthy}`);
160
161// ===========================================
162// 5. CHAINING METHODS
163// ===========================================
164console.log("\n=== 5. Method chaining ===");
165
166// Example 1: filter + map + sort
167const topHealthyCarnivores = dinosaurs
168 .filter(d => d.carnivore)
169 .filter(d => d.health >= 80)
170 .map(d => ({ name: d.name, health: d.health }))
171 .sort((a, b) => b.health - a.health)
172 .slice(0, 3);
173
174console.log("\nTOP 3 healthy carnivores:");
175topHealthyCarnivores.forEach((d, i) => {
176 console.log(` ${i + 1}. ${d.name} - ${d.health}%`);
177});
178
179// Example 2: filter + map + reduce
180const heavyHerbivoreWeight = dinosaurs
181 .filter(d => !d.carnivore)
182 .filter(d => d.weight > 1000)
183 .map(d => d.weight)
184 .reduce((sum, weight) => sum + weight, 0);
185
186console.log(`\nTotal weight of heavy herbivores: ${heavyHerbivoreWeight}kg`);
187
188// Example 3: Complex processing
189const sectorReport = Object.entries(
190 dinosaurs
191 .filter(d => d.health >= 70)
192 .reduce((acc, d) => {
193 if (!acc[d.sector]) {
194 acc[d.sector] = { count: 0, avgHealth: 0, totalHealth: 0 };
195 }
196 acc[d.sector].count++;
197 acc[d.sector].totalHealth += d.health;
198 return acc;
199 }, {})
200)
201.map(([sector, data]) => ({
202 sector,
203 count: data.count,
204 avgHealth: (data.totalHealth / data.count).toFixed(1)
205}))
206.sort((a, b) => b.avgHealth - a.avgHealth);
207
208console.log("\nSector report (health β₯70%):");
209sectorReport.forEach(s => {
210 console.log(` Sector ${s.sector}: ${s.count} dinosaurs, average health: ${s.avgHealth}%`);
211});
212
213// ===========================================
214// 6. COMPARING APPROACHES
215// ===========================================
216console.log("\n=== 6. Comparing approaches ===");
217
218console.log("\nTask: Find the names of carnivores in sector B");
219
220// Way 1: for loop (imperative)
221const result1 = [];
222for (let i = 0; i < dinosaurs.length; i++) {
223 if (dinosaurs[i].carnivore && dinosaurs[i].sector === "B") {
224 result1.push(dinosaurs[i].name);
225 }
226}
227console.log("For loop:", result1);
228
229// Way 2: forEach (partly functional)
230const result2 = [];
231dinosaurs.forEach(dino => {
232 if (dino.carnivore && dino.sector === "B") {
233 result2.push(dino.name);
234 }
235});
236console.log("forEach:", result2);
237
238// Way 3: filter + map (functional)
239const result3 = dinosaurs
240 .filter(d => d.carnivore && d.sector === "B")
241 .map(d => d.name);
242console.log("filter + map:", result3);
243
244// ===========================================
245// 7. COMPLEX EXAMPLE
246// ===========================================
247console.log("\n=== 7. Reporting system - complex example ===");
248
249class ParkReportingSystem {
250 constructor(dinosaurs) {
251 this.dinosaurs = dinosaurs;
252 }
253
254 // Main dashboard
255 getDashboard() {
256 const total = this.dinosaurs.length;
257
258 const healthStats = this.dinosaurs.reduce((acc, d) => {
259 if (d.health >= 90) acc.excellent++;
260 else if (d.health >= 70) acc.good++;
261 else acc.poor++;
262 acc.total += d.health;
263 return acc;
264 }, { excellent: 0, good: 0, poor: 0, total: 0 });
265
266 const feedingStats = this.dinosaurs.reduce((acc, d) => {
267 if (d.fed) acc.fed++;
268 else acc.hungry++;
269 return acc;
270 }, { fed: 0, hungry: 0 });
271
272 return {
273 totalDinosaurs: total,
274 avgHealth: (healthStats.total / total).toFixed(1),
275 healthBreakdown: {
276 excellent: healthStats.excellent,
277 good: healthStats.good,
278 poor: healthStats.poor
279 },
280 feeding: feedingStats
281 };
282 }
283
284 // Critical alerts
285 getCriticalAlerts() {
286 return this.dinosaurs
287 .filter(d => d.health < 50 || (d.carnivore && !d.fed))
288 .map(d => ({
289 name: d.name,
290 sector: d.sector,
291 issue: d.health < 50 ? `Critical health: ${d.health}%` : "Hungry carnivore!",
292 priority: d.health < 50 && d.carnivore && !d.fed ? "EXTREME" : "HIGH"
293 }))
294 .sort((a, b) => {
295 const priorityOrder = { "EXTREME": 0, "HIGH": 1 };
296 return priorityOrder[a.priority] - priorityOrder[b.priority];
297 });
298 }
299
300 // Health ranking
301 getHealthRanking(limit = 5) {
302 return this.dinosaurs
303 .map(d => ({
304 name: d.name,
305 species: d.species,
306 health: d.health,
307 sector: d.sector
308 }))
309 .sort((a, b) => b.health - a.health)
310 .slice(0, limit);
311 }
312
313 // Sector analysis
314 getSectorAnalysis() {
315 const sectorData = this.dinosaurs.reduce((acc, d) => {
316 if (!acc[d.sector]) {
317 acc[d.sector] = {
318 dinosaurs: [],
319 totalHealth: 0,
320 carnivores: 0,
321 unfed: 0
322 };
323 }
324 acc[d.sector].dinosaurs.push(d.name);
325 acc[d.sector].totalHealth += d.health;
326 if (d.carnivore) acc[d.sector].carnivores++;
327 if (!d.fed) acc[d.sector].unfed++;
328 return acc;
329 }, {});
330
331 return Object.entries(sectorData)
332 .map(([sector, data]) => ({
333 sector,
334 count: data.dinosaurs.length,
335 avgHealth: (data.totalHealth / data.dinosaurs.length).toFixed(1),
336 carnivores: data.carnivores,
337 unfed: data.unfed,
338 dinosaurs: data.dinosaurs
339 }))
340 .sort((a, b) => a.sector.localeCompare(b.sector));
341 }
342
343 // Display full report
344 displayFullReport() {
345 console.log("\n" + "=".repeat(70));
346 console.log("JURASSIC PARK - COMPREHENSIVE REPORT");
347 console.log("=".repeat(70));
348
349 // Dashboard
350 const dashboard = this.getDashboard();
351 console.log("\nDASHBOARD");
352 console.log(` Total dinosaurs: ${dashboard.totalDinosaurs}`);
353 console.log(` Average health: ${dashboard.avgHealth}%`);
354 console.log(` Health - Excellent: ${dashboard.healthBreakdown.excellent}, Good: ${dashboard.healthBreakdown.good}, Poor: ${dashboard.healthBreakdown.poor}`);
355 console.log(` Feeding - Fed: ${dashboard.feeding.fed}, Hungry: ${dashboard.feeding.hungry}`);
356
357 // Alerts
358 const alerts = this.getCriticalAlerts();
359 if (alerts.length > 0) {
360 console.log("\nCRITICAL ALERTS");
361 alerts.forEach((alert, i) => {
362 console.log(` ${i + 1}. [${alert.priority}] ${alert.name} (Sector ${alert.sector}): ${alert.issue}`);
363 });
364 } else {
365 console.log("\nNo critical alerts");
366 }
367
368 // Ranking
369 const ranking = this.getHealthRanking(5);
370 console.log("\nTOP 5 - HEALTH RANKING");
371 ranking.forEach((d, i) => {
372 console.log(` ${i + 1}. ${d.name} (${d.species}) - ${d.health}% - Sector ${d.sector}`);
373 });
374
375 // Sector analysis
376 const sectorAnalysis = this.getSectorAnalysis();
377 console.log("\nSECTOR ANALYSIS");
378 sectorAnalysis.forEach(sector => {
379 console.log(` Sector ${sector.sector}:`);
380 console.log(` Dinosaurs: ${sector.count} - Average health: ${sector.avgHealth}%`);
381 console.log(` Carnivores: ${sector.carnivores}, Hungry: ${sector.unfed}`);
382 console.log(` List: ${sector.dinosaurs.join(", ")}`);
383 });
384 }
385}
386
387// Run the reporting system
388const reportSystem = new ParkReportingSystem(dinosaurs);
389reportSystem.displayFullReport();
390
391console.log("\nCongratulations! You've mastered advanced iterations!");Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is the key difference between map() and forEach()?
2. What is the reduce() method on an array used for?
Hands-on tasks in the game
- Horizontal ordering
Arrange the elements of filtering and mapping an array: