We use cookies to enhance your experience on the site
CodeWorlds

Final Project: Dinosaur Monitoring System

In Jurassic Park, we need a comprehensive system to monitor all dinosaurs. In this project, you'll build a fully functional monitoring system using everything you've learned: arrays, array methods (filter, map, reduce, find), functions with default and rest parameters, object methods, and method chaining.

The Assignment

You need to build a

DinosaurMonitor
system that:

  1. Stores dinosaur data as an array of objects
  2. Provides analysis methods using filter, map, reduce, find
  3. Generates reports with method chaining
  4. Handles feeding with default and rest parameters
  5. Tracks incidents with a log

Project Goals

You'll build a monitoring system that:

  • Stores dinosaur data in arrays of objects
  • Uses functions with different parameter types (default, rest)
  • Applies arrow functions for concise code
  • Filters, maps, and reduces data using array methods
  • Properly manages variable scope
  • Uses objects with methods to model the system

1. Dinosaur Database - Arrays and Objects

The first step is creating the park's database. Each dinosaur is an object with properties, and the entire population is stored in an array.

1// Park 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 dinosaur count:", dinosaurs.length);

2. Filtering and Analysis - Array Methods

The system needs to enable quick filtering of the population by various criteria. Here you'll use

filter
,
map
,
reduce
, and
find
.

1// Filtering carnivores with filter
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Carnivores:", carnivores.map(d => d.name));
4
5// Filtering unhealthy dinosaurs
6const unhealthyDinos = dinosaurs.filter(dino => !dino.isHealthy);
7console.log("Need treatment:", unhealthyDinos.map(d => d.name));
8
9// Mapping - creating a weight report
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// Reduce - calculating total population weight
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 - searching 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 unhealthy:", anyUnhealthy);

3. Monitoring Functions - Parameters, Scope, and this

The system needs a set of monitoring functions with different parameter types. Pay attention to variable scope and the difference between regular and arrow functions in the context of

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"); // uses default values
9generateAlert("Blue", "DANGER"); // priority will be "normal"
10
11// Function with rest parameter - logging multiple events
12const logEvents = (enclosure, ...events) => {
13  console.log("Enclosure " + enclosure + " - " + events.length + " events registered:");
14  events.forEach((event, index) => {
15    console.log("  " + (index + 1) + ". " + event);
16  });
17};
18
19logEvents("A-1", "Feeding at 8:00", "Veterinary check", "Movement 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 fence in 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 throughout the 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 status = "ELEVATED"; // available only in this if block
52      console.log(dino.name + ": heart rate " + dino.heartRate + " - " + status);
53    }
54  }
55
56  // console.log(status); // ERROR! status doesn't exist outside the if block
57  // console.log(dino);   // ERROR! dino doesn't exist outside the for loop
58  console.log("Alert threshold:", threshold); // OK - threshold is in function scope
59}
60
61monitorHeartRates();

4. Advanced Processing - Method Chaining

In a real monitoring system, we often need to chain multiple operations together. Array methods are perfect for this.

1// Method chaining - security report
2const securityReport = dinosaurs
3  .filter(dino => dino.diet === "carnivore") // only carnivores
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 using 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("=== DIET GROUPS ===");
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("Unhealthy:", stats.unhealthy);
60console.log("Average heart rate:", stats.avgHeartRate, "bpm");
61console.log("Heaviest:", stats.heaviest);
62console.log("Lightest:", stats.lightest);

5. Complete System - DinosaurMonitor Class

Now let's combine everything into a comprehensive class-based monitoring system.

System Structure

1const parkData = [
2  { name: 'Rex', species: 'T-Rex', zone: 'A', health: 95, weight: 8000, dangerous: true, hunger: 30 },
3  { name: 'Blue', species: 'Velociraptor', zone: 'B', health: 40, weight: 15, dangerous: true, hunger: 80 },
4  { name: 'Stego', species: 'Stegosaurus', zone: 'C', health: 80, weight: 3500, dangerous: false, hunger: 20 },
5  { name: 'Brachi', species: 'Brachiosaurus', zone: 'C', health: 90, weight: 25000, dangerous: false, hunger: 50 },
6  { name: 'Delta', species: 'Velociraptor', zone: 'B', health: 85, weight: 14, dangerous: true, hunger: 60 },
7  { name: 'Echo', species: 'Velociraptor', zone: 'B', health: 30, weight: 13, dangerous: true, hunger: 90 }
8];
9
10class DinosaurMonitor {
11  constructor(dinosaurs) {
12    // Deep copy so we don't modify the original
13    this.dinosaurs = dinosaurs.map(d => ({ ...d }));
14    this.incidents = [];
15  }
16
17  // Find dinosaurs needing attention (health < threshold)
18  findCritical(threshold = 50) {
19    return this.dinosaurs.filter(d => d.health < threshold);
20  }
21
22  // Get dinosaurs in a specific zone
23  getByZone(zone) {
24    return this.dinosaurs.filter(d => d.zone === zone);
25  }
26
27  // Feed one or more dinosaurs (rest parameters)
28  feed(amount = 30, ...dinoNames) {
29    const targets = dinoNames.length > 0
30      ? this.dinosaurs.filter(d => dinoNames.includes(d.name))
31      : this.dinosaurs; // feed all if no names given
32
33    targets.forEach(d => {
34      d.hunger = Math.max(0, d.hunger - amount);
35    });
36
37    console.log(`Fed ${targets.length} dinosaurs (${amount}% hunger reduction)`);
38    return this;
39  }
40
41  // Calculate park statistics
42  getStats() {
43    const total = this.dinosaurs.length;
44    const avgHealth = this.dinosaurs.reduce((sum, d) => sum + d.health, 0) / total;
45    const critical = this.dinosaurs.filter(d => d.health < 50).length;
46    const totalWeight = this.dinosaurs.reduce((sum, d) => sum + d.weight, 0);
47
48    return {
49      total,
50      avgHealth: avgHealth.toFixed(1),
51      critical,
52      totalWeight,
53      dangerous: this.dinosaurs.filter(d => d.dangerous).length
54    };
55  }
56
57  // Generate zone report using method chaining internally
58  generateZoneReport() {
59    const zones = [...new Set(this.dinosaurs.map(d => d.zone))].sort();
60
61    return zones.map(zone => {
62      const zoneDinos = this.dinosaurs.filter(d => d.zone === zone);
63      return {
64        zone,
65        count: zoneDinos.length,
66        avgHealth: (zoneDinos.reduce((s, d) => s + d.health, 0) / zoneDinos.length).toFixed(1),
67        hungry: zoneDinos.filter(d => d.hunger > 70).map(d => d.name)
68      };
69    });
70  }
71
72  // Log an incident
73  logIncident(type, description, ...affectedDinos) {
74    const incident = {
75      id: this.incidents.length + 1,
76      type,
77      description,
78      affectedDinos,
79      timestamp: new Date().toISOString()
80    };
81    this.incidents.push(incident);
82    console.log(`Incident #${incident.id} logged: ${type}`);
83    return this;
84  }
85
86  // Generate summary report
87  report() {
88    const stats = this.getStats();
89    const critical = this.findCritical();
90
91    console.log('=== JURASSIC PARK STATUS REPORT ===');
92    console.log(`Total dinosaurs: ${stats.total}`);
93    console.log(`Average health: ${stats.avgHealth}%`);
94    console.log(`Critical (health < 50): ${stats.critical}`);
95
96    if (critical.length > 0) {
97      console.log('Critical dinosaurs:');
98      critical.forEach(d => console.log(`  - ${d.name} (${d.species}): ${d.health}%`));
99    }
100
101    console.log(`Total incidents logged: ${this.incidents.length}`);
102    return this;
103  }
104}
105
106// Usage
107const monitor = new DinosaurMonitor(parkData);
108
109// Chain operations
110monitor
111  .feed(40, 'Blue', 'Echo')  // Feed the most hungry
112  .logIncident('HEALTH', 'Multiple dinosaurs critical', 'Blue', 'Echo')
113  .report();
114
115// Zone analysis
116const zoneReports = monitor.generateZoneReport();
117zoneReports.forEach(r => {
118  console.log(`Zone ${r.zone}: ${r.count} dinos, avg health ${r.avgHealth}%`);
119  if (r.hungry.length > 0) console.log(`  Hungry: ${r.hungry.join(', ')}`);
120});

Implementation Tips

When building your monitoring system, pay attention to:

  1. Arrow functions vs regular - use arrow functions for callbacks (
    filter
    ,
    map
    ,
    reduce
    ), and regular functions for object methods that need
    this
  2. Default parameters - useful when you don't always have all the data (e.g., alert priority)
  3. Rest parameter (
    ...args
    )
    - ideal when the number of arguments varies (e.g., event list)
  4. Method chaining -
    filter().map().sort()
    is more readable than nested loops
  5. Variable scope -
    let
    and
    const
    have block scope,
    var
    has function scope - choose wisely
  6. Array methods don't mutate the original -
    filter
    ,
    map
    ,
    reduce
    create new arrays, but
    push
    ,
    splice
    ,
    sort
    modify the original
  7. Objects as data structures - group related data in objects instead of keeping them in separate variables

Good luck!

Time to step into the role of the head operator of Jurassic Park! Create 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 said: "We spared no expense" - and you shouldn't spare any code!

"This monitoring system is Jurassic Park's backbone" - says Dr. Rex. "Notice how we chain operations, use default parameters for flexibility, and rest parameters for variable input. The array methods make complex analysis readable and elegant. This is real JavaScript in action!"

Go to CodeWorlds