JavaScript and TypeScript course Β· Module 2: Functions, Arrays and Objects
Final Project: Dinosaur Monitoring System
In this lesson4
Welcome back to the Jurassic Park command center! In this module you have learned about functions, arrays, variable scope, hoisting, the this keyword, objects and prototypes. Now it is time to combine this knowledge into one integrated Dinosaur Monitoring System - the operational heart of the whole park.
Imagine a central console where the park operators track the location, health and behavior of every dinosaur in real time. Sensors placed all over the park send data that our system has to process, filter and analyze. Without an efficient monitoring system, the park would quickly spiral out of control - just like in the movie!
In this project you will build a complete system that uses arrow functions to process data, array methods to filter and transform it, higher-order functions for flexible logic, and objects to model the park's entities.
Project Goal
You will build a monitoring system that:
- Stores dinosaur data in arrays of objects
- Uses functions with different types of parameters (default, rest)
- Uses arrow functions for concise code
- Filters, maps and reduces data with array methods
- Manages variable scope correctly
- Uses objects with methods to model the system
Functional Requirements
1. Dinosaur Database - Arrays and Objects
The first step is to create the park's database. Each dinosaur is an object with properties, and the whole population is stored in an array.
1// The park's dinosaur database
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// Adding a new dinosaur to the database
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("Registered:", newDino.name, "(" + newDino.species + ")");
76 return newDino;
77};
78
79addDinosaur("Echo", "Velociraptor", 68, "carnivore", "C-2");
80console.log("Total number of dinosaurs:", dinosaurs.length);2. Filtering and Analysis - Array Methods
The system must make it possible to filter the population quickly by various criteria. This is where you will use filter, map, reduce and find.
1// Filtering predators with filter
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Predators:", carnivores.map(d => d.name));
4
5// Filtering sick dinosaurs
6const unhealthyDinos = dinosaurs.filter(dino => !dino.isHealthy);
7console.log("Need treatment:", unhealthyDinos.map(d => d.name));
8
9// Mapping - creating a report with names and weights
10const weightReport = dinosaurs.map(dino => {
11 return {
12 name: dino.name,
13 weight: dino.weight,
14 category: dino.weight > 5000 ? "heavy" : "light"
15 };
16});
17console.log("Weight report:");
18weightReport.forEach(entry => {
19 console.log(" " + entry.name + ": " + entry.weight + " kg (" + entry.category + ")");
20});
21
22// Reducing - calculating the total weight of all dinosaurs
23const totalWeight = dinosaurs.reduce((sum, dino) => sum + dino.weight, 0);
24console.log("Total population weight:", totalWeight, "kg");
25
26// Average weight
27const avgWeight = totalWeight / dinosaurs.length;
28console.log("Average weight:", Math.round(avgWeight), "kg");
29
30// Finding the heaviest dinosaur
31const heaviest = dinosaurs.reduce((max, dino) =>
32 dino.weight > max.weight ? dino : max
33);
34console.log("Heaviest:", heaviest.name, "-", heaviest.weight, "kg");
35
36// find - looking for a specific dinosaur
37const blue = dinosaurs.find(dino => dino.name === "Blue");
38console.log("Found:", blue ? blue.name + " in " + blue.enclosure : "not found");
39
40// some and every - checking conditions
41const allHealthy = dinosaurs.every(dino => dino.isHealthy);
42const anyUnhealthy = dinosaurs.some(dino => !dino.isHealthy);
43console.log("All healthy:", allHealthy);
44console.log("Any sick dinosaurs:", anyUnhealthy);3. Monitoring Functions - Parameters, Scope and this
The system needs a set of monitoring functions with different types of parameters. Pay attention to variable scope and to the difference between regular functions and arrow functions when it comes to this.
1// Function with default parameters
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"); // will use the default values
9generateAlert("Blue", "DANGER"); // priority will be "normal"
10
11// Function with a rest parameter - logging multiple events
12const logEvents = (enclosure, ...events) => {
13 console.log("Enclosure " + enclosure + " - logged " + events.length + " events:");
14 events.forEach((event, index) => {
15 console.log(" " + (index + 1) + ". " + event);
16 });
17};
18
19logEvents("A-1", "Feeding at 8:00", "Veterinary check-up", "Physical activity");
20logEvents("C-2", "Escape attempt!");
21
22// Monitor object with methods - this in context
23const enclosureMonitor = {
24 sector: "A",
25 alertCount: 0,
26
27 // Traditional method - this points to the object
28 checkFence: function() {
29 this.alertCount++;
30 console.log("Checking the fence of sector " + this.sector +
31 " (alert #" + this.alertCount + ")");
32 },
33
34 // ES6 shorthand method
35 getStatus() {
36 return "Sector " + this.sector + " - " + this.alertCount + " alerts";
37 }
38};
39
40enclosureMonitor.checkFence();
41enclosureMonitor.checkFence();
42console.log(enclosureMonitor.getStatus());
43
44// Variable scope - block vs function
45function monitorHeartRates() {
46 const threshold = 100; // available in the whole function
47
48 for (let i = 0; i < dinosaurs.length; i++) {
49 const dino = dinosaurs[i]; // available only in this block
50 if (dino.heartRate > threshold) {
51 let pulseStatus = "ELEVATED"; // available only in this if
52 console.log(dino.name + ": heart rate " + dino.heartRate + " - " + pulseStatus);
53 }
54 }
55
56 // console.log(pulseStatus); // ERROR! pulseStatus does not exist outside the if block
57 // console.log(dino); // ERROR! dino does not exist outside the for loop
58 console.log("Alert threshold:", threshold); // OK - threshold is in the function scope
59}
60
61monitorHeartRates();4. Advanced Processing - Chaining Methods
In a real monitoring system we often need to combine many operations into a chain. Array methods are perfect for this.
1// Method chain - security report
2const securityReport = dinosaurs
3 .filter(dino => dino.diet === "carnivore") // predators only
4 .map(dino => ({ // data transformation
5 name: dino.name,
6 enclosure: dino.enclosure,
7 danger: dino.weight > 1000 ? "HIGH" : "MEDIUM"
8 }))
9 .sort((a, b) => a.name.localeCompare(b.name)); // sorting
10
11console.log("=== SECURITY REPORT ===");
12securityReport.forEach(entry => {
13 console.log(entry.name + " [" + entry.enclosure + "] - threat: " + entry.danger);
14});
15
16// Grouping dinosaurs by diet with 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("=== BREAKDOWN BY DIET ===");
27console.log("Herbivores:", groupedByDiet.herbivore);
28console.log("Carnivores:", groupedByDiet.carnivore);
29
30// Higher-order function - creating filters
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("Carnivores:", carnivoreDinos.map(d => d.name));
42console.log("Sector B-3:", sectorBDinos.map(d => d.name));
43
44// Population statistics
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("=== POPULATION STATISTICS ===");
57console.log("Total:", stats.total);
58console.log("Healthy:", stats.healthy);
59console.log("Sick:", stats.unhealthy);
60console.log("Average heart rate:", stats.avgHeartRate, "bpm");
61console.log("Heaviest:", stats.heaviest);
62console.log("Lightest:", stats.lightest);Implementation Tips
As you build your monitoring system, pay attention to:
- Arrow functions vs regular functions - use arrow functions for callbacks (
filter,map,reduce) and regular functions for object methods that needthis - Default parameters - useful when we do not always have all the data (e.g. the alert priority)
- The rest parameter (
...args) - ideal when the number of arguments varies (e.g. a list of events) - Method chaining -
filter().map().sort()is more readable than nested loops - Variable scope -
letandconsthave block scope,varhas function scope - choose consciously - Array methods and the original -
filterandmapcreate new arrays andreducereturns a new value, whilepush,spliceandsortmodify the original - Objects as data structures - group related data into objects instead of keeping it in separate variables
Good Luck!
Time to step into the role of Jurassic Park's chief operator! Build your own Monitoring System in the editor below. Combine arrays, functions, array methods and objects into one working system. Remember - every dinosaur in the park depends on your code. As John Hammond used to say: "We spared no expense" - so don't spare any code either!
Code for this lesson: index.js
1// Final project: Dinosaur monitoring system
2// Combine: functions, arrays, arrow functions, scope, this, objects
3
4// === 1. PARK DATABASE ===
5const dinosaurs = [
6 { id: 1, name: "Rex", species: "Tyrannosaurus", weight: 8500, diet: "carnivore", enclosure: "A-1", isHealthy: true, heartRate: 72 },
7 { id: 2, name: "Tricia", species: "Triceratops", weight: 6200, diet: "herbivore", enclosure: "B-3", isHealthy: true, heartRate: 55 },
8 { id: 3, name: "Blue", species: "Velociraptor", weight: 75, diet: "carnivore", enclosure: "C-2", isHealthy: false, heartRate: 110 },
9];
10
11// === 2. MONITORING SYSTEM ===
12// TODO: Create a monitoringSystem object with methods using this
13const monitoringSystem = {
14 parkName: "Jurassic Park",
15 alerts: [],
16
17 // TODO: Method filtering dinosaurs by diet (use an arrow function)
18 filterByDiet: (dinos, diet) => {
19 // TODO: Use filter()
20 },
21
22 // TODO: Method generating a health report (use map)
23 getHealthReport(dinos) {
24 // TODO: Use map() to create a report for each dinosaur
25 // Return an array of objects: { name, status, heartRate }
26 },
27
28 // TODO: Method calculating average weight (use reduce)
29 calculateAverageWeight(dinos) {
30 // TODO: Use reduce() to calculate the average
31 },
32
33 // TODO: Method adding an alert
34 addAlert(message, severity) {
35 // TODO: Add an alert with a timestamp to this.alerts
36 },
37};
38
39// === 3. HIGHER-ORDER FUNCTIONS ===
40// TODO: Create a function createFilter that returns a filtering function
41function createFilter(property, value) {
42 // TODO: Return an arrow function that filters by the given property
43}
44
45// === 4. FINAL REPORT ===
46// TODO: Generate and display a full monitoring report
47// - List of dinosaurs by diet
48// - Health report
49// - Average weight
50// - Active alerts
51
52console.log("Monitoring system started!");Spotted a mistake in this lesson?