> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/NormandoRamirezDelgado/6C-Febrero-2026/llms.txt
> Use this file to discover all available pages before exploring further.

# Student Management System

> Build an advanced student grade management system using Maps and Lists

## Exercise Overview

This advanced exercise demonstrates how to work with complex data structures in Dart, specifically using `Map<String, List<int>>` to create a comprehensive student grade management system.

### What You'll Build

A system that manages student grades by subject and provides:

<CardGroup cols={2}>
  <Card title="Grade Tracking" icon="chart-line">
    Store multiple grades for each subject
  </Card>

  <Card title="Average Calculation" icon="calculator">
    Calculate the average grade for each subject
  </Card>

  <Card title="Performance Analysis" icon="magnifying-glass-chart">
    Find the best and worst performing subjects
  </Card>

  <Card title="Complete Reports" icon="file-chart-column">
    Generate comprehensive grade reports
  </Card>
</CardGroup>

## Problem Statement

Given a Map containing subjects and their corresponding grades:

```dart theme={null}
Map<String, List<int>> calificacionesPorMateria = {
  'Matemáticas': [85, 90, 88],
  'Español': [92, 88, 95],
  'Inglés': [78, 82, 80],
  'Programación': [95, 98, 96]
};
```

Create a system that:

<Steps>
  <Step title="Calculate subject averages">
    Compute the average grade for each subject
  </Step>

  <Step title="Find best performance">
    Identify the subject with the highest average
  </Step>

  <Step title="Find worst performance">
    Identify the subject with the lowest average
  </Step>

  <Step title="Generate complete report">
    Display all subjects with their grades and averages
  </Step>
</Steps>

## Solution Architecture

### Data Structure

<CodeGroup>
  ```dart Main Data theme={null}
  Map<String, List<int>> calificacionesPorMateria = {
    'Matemáticas'   : [85, 90, 88],
    'Español'       : [92, 88, 95],
    'Inglés'        : [78, 82, 80],
    'Programación'  : [95, 98, 96]
  };
  ```

  ```dart Average Storage theme={null}
  Map<String, double> promedios = {};
  ```
</CodeGroup>

<Note>
  We use two Maps: one to store the raw grades (`Map<String, List<int>>`) and another to store the calculated averages (`Map<String, double>`).
</Note>

### Function 1: Calculate Subject Averages

```dart theme={null}
void promedioMateria(Map<String, List<int>> calificacionesPorMateria, Map<String, double> promedios){
  // - Calcula el promedio de cada materia
  calificacionesPorMateria.forEach((materia, calificaciones){
    int suma = 0;
    for (var calificacion in calificaciones) {
      suma += calificacion;
    }
    double promedio = suma / calificaciones.length;
    promedios[materia] = promedio;
  });
}
```

<Accordion title="Understanding forEach with Maps">
  The `forEach` method on a Map provides two parameters:

  * `materia`: The key (subject name)
  * `calificaciones`: The value (list of grades)

  This allows us to iterate through each subject and its grades simultaneously.

  ```dart theme={null}
  // For each entry in the map:
  'Matemáticas' : [85, 90, 88]
  // materia = 'Matemáticas'
  // calificaciones = [85, 90, 88]
  ```
</Accordion>

### Function 2: Find Best Performance

```dart theme={null}
void obtenerMejorPromedio(Map<String, double> promedios) {
  // - Encuentra la materia con el mejor promedio
  double mejorPromedio = 0.0;
  String mejorMateria = '';
  promedios.forEach((materia, promedio){
    if (promedio > mejorPromedio) {
      mejorPromedio = promedio;
      mejorMateria = materia;
    }
  });
  print('\nMATERIA CON MEJOR PROMEDIO: $mejorMateria');
}
```

<Tip>
  This function uses a simple algorithm: start with 0.0 as the best average, then update it whenever we find a higher one.
</Tip>

### Function 3: Find Worst Performance

```dart theme={null}
void obtenerPeorPromedio(Map<String, double> promedios) {
  // - Encuentra la materia con el peor promedio
  double peorPromedio = 100.0;
  String peorMateria = '';
  promedios.forEach((materia, promedio){
    if (promedio < peorPromedio) {
      peorPromedio = promedio;
      peorMateria = materia;
    }
  });
  print('\nMATERIA CON PEOR PROMEDIO: $peorMateria');
}
```

<Note>
  We initialize `peorPromedio` to 100.0 (the maximum possible grade) so any actual grade will be lower.
</Note>

### Function 4: Display Complete Report

```dart theme={null}
void reporteCompleto(Map<String, List<int>> calificacionesPorMateria) {
  // - Imprime reporte completo
  print('\n==== MATERIAS CON SUS CALIFICACIONES ====');
  calificacionesPorMateria.forEach((materia, calificaciones){
    print('Materia: $materia \t- \t$calificaciones');
  });
  print('');
}
```

### Function 5: Display Averages

```dart theme={null}
void imprimirPromedios(Map<String, double> promedios) {
  print('\n==== MATERIAS CON SUS PROMEDIOS ====');
  promedios.forEach((materia, promedio){
    print('Materia: $materia \t- ${promedio.toStringAsFixed(2)}');
  });
  print('');
}
```

<Tip>
  The `toStringAsFixed(2)` method formats the decimal number to show exactly 2 decimal places, making the output more readable.
</Tip>

## Complete Program

```dart theme={null}
/* Sistema de calificaciones por materia

  Map<String, List<int>> calificacionesPorMateria = {
    'Matemáticas': [85, 90, 88],
    'Español': [92, 88, 95],
    'Inglés': [78, 82, 80],
    'Programación': [95, 98, 96]
  };
  
  - Calcula el promedio de cada materia
  - Encuentra la materia con el mejor promedio
  - Encuentra la materia con el peor promedio
  - Imprime reporte completo
*/

void promedioMateria(Map<String, List<int>> calificacionesPorMateria, Map<String, double> promedios){
  // - Calcula el promedio de cada materia
  calificacionesPorMateria.forEach((materia, calificaciones){
    int suma = 0;
    for (var calificacion in calificaciones) {
      suma += calificacion;
    }
    double promedio = suma / calificaciones.length;
    promedios[materia] = promedio;
  });
}

void obtenerMejorPromedio(Map<String, double> promedios) {
  // - Encuentra la materia con el mejor promedio
  double mejorPromedio = 0.0;
  String mejorMateria = '';
  promedios.forEach((materia, promedio){
    if (promedio > mejorPromedio) {
      mejorPromedio = promedio;
      mejorMateria = materia;
    }
  });
  print('\nMATERIA CON MEJOR PROMEDIO: $mejorMateria');
}

void obtenerPeorPromedio(Map<String, double> promedios) {
  // - Encuentra la materia con el peor promedio
  double peorPromedio = 100.0;
  String peorMateria = '';
  promedios.forEach((materia, promedio){
    if (promedio < peorPromedio) {
      peorPromedio = promedio;
      peorMateria = materia;
    }
  });
  print('\nMATERIA CON PEOR PROMEDIO: $peorMateria');
}

void reporteCompleto(Map<String, List<int>> calificacionesPorMateria) {
  // - Imprime reporte completo
  print('\n==== MATERIAS CON SUS CALIFICACIONES ====');
  calificacionesPorMateria.forEach((materia, calificaciones){
    print('Materia: $materia \t- \t$calificaciones');
  });
  print('');

}

void imprimirPromedios(Map<String, double> promedios) {
  print('\n==== MATERIAS CON SUS PROMEDIOS ====');
  promedios.forEach((materia, promedio){
    print('Materia: $materia \t- ${promedio.toStringAsFixed(2)}');
  });
  print('');
}

void main() {
  Map<String, List<int>> calificacionesPorMateria = {
    'Matemáticas'   : [85, 90, 88],
    'Español'       : [92, 88, 95],
    'Inglés'        : [78, 82, 80],
    'Programación'  : [95, 98, 96]
  };

  Map<String, double> promedios = {};

  promedioMateria(calificacionesPorMateria, promedios);
  imprimirPromedios(promedios);
  obtenerMejorPromedio(promedios);
  obtenerPeorPromedio(promedios);
  reporteCompleto(calificacionesPorMateria);
}
```

## Sample Output

```
==== MATERIAS CON SUS PROMEDIOS ====
Materia: Matemáticas    - 87.67
Materia: Español        - 91.67
Materia: Inglés         - 80.00
Materia: Programación   - 96.33

MATERIA CON MEJOR PROMEDIO: Programación

MATERIA CON PEOR PROMEDIO: Inglés

==== MATERIAS CON SUS CALIFICACIONES ====
Materia: Matemáticas    -   [85, 90, 88]
Materia: Español        -   [92, 88, 95]
Materia: Inglés         -   [78, 82, 80]
Materia: Programación   -   [95, 98, 96]
```

## Key Concepts

<CardGroup cols={2}>
  <Card title="Map Data Structure" icon="map">
    Maps store key-value pairs for efficient data lookup
  </Card>

  <Card title="Nested Collections" icon="layer-group">
    `Map<String, List<int>>` combines Maps and Lists
  </Card>

  <Card title="forEach Method" icon="arrows-rotate">
    Iterate through Map entries with key-value pairs
  </Card>

  <Card title="Data Aggregation" icon="chart-simple">
    Calculate statistics from collections of data
  </Card>
</CardGroup>

## Advanced Concepts

<AccordionGroup>
  <Accordion title="Why use two separate Maps?">
    We use two Maps because:

    1. **calificacionesPorMateria**: Stores the raw data (all individual grades)
    2. **promedios**: Stores the calculated averages

    This separation allows us to:

    * Keep the original data intact
    * Access both raw grades and averages when needed
    * Recalculate averages if grades change

    ```dart theme={null}
    // Original data preserved
    print(calificacionesPorMateria['Matemáticas']); // [85, 90, 88]

    // Calculated average available
    print(promedios['Matemáticas']); // 87.67
    ```
  </Accordion>

  <Accordion title="Alternative: Using Map methods">
    Dart provides functional programming methods that can simplify the code:

    ```dart theme={null}
    // Using reduce for sum
    double calcularPromedio(List<int> calificaciones) {
      int suma = calificaciones.reduce((a, b) => a + b);
      return suma / calificaciones.length;
    }

    // Using map to transform values
    void promedioMateria(Map<String, List<int>> calificaciones, Map<String, double> promedios){
      calificaciones.forEach((materia, notas){
        promedios[materia] = calcularPromedio(notas);
      });
    }
    ```
  </Accordion>

  <Accordion title="Handling edge cases">
    In a production system, consider:

    ```dart theme={null}
    void promedioMateria(Map<String, List<int>> calificaciones, Map<String, double> promedios){
      calificaciones.forEach((materia, notas){
        // Check for empty lists
        if (notas.isEmpty) {
          promedios[materia] = 0.0;
          return;
        }
        
        int suma = 0;
        for (var nota in notas) {
          suma += nota;
        }
        promedios[materia] = suma / notas.length;
      });
    }
    ```
  </Accordion>
</AccordionGroup>

## Practice Challenges

<CardGroup cols={2}>
  <Card title="Challenge 1: Overall Average" icon="1">
    Add a function to calculate the overall average across all subjects.
  </Card>

  <Card title="Challenge 2: Grade Distribution" icon="2">
    Create a function that shows how many grades fall into different ranges (90-100, 80-89, etc.).
  </Card>

  <Card title="Challenge 3: Student Class" icon="3">
    Expand the system to manage multiple students, each with their own subject grades.
  </Card>

  <Card title="Challenge 4: Grade Input" icon="4">
    Add functionality to let users input new subjects and grades interactively.
  </Card>
</CardGroup>

## Enhanced Version: Multiple Students

<Accordion title="Click to see an enhanced version managing multiple students">
  ```dart theme={null}
  void main() {
    // Map of students, each with their subject grades
    Map<String, Map<String, List<int>>> estudiantes = {
      'Juan': {
        'Matemáticas': [85, 90, 88],
        'Español': [92, 88, 95],
      },
      'María': {
        'Matemáticas': [95, 92, 98],
        'Español': [88, 90, 92],
      }
    };
    
    // Calculate average for each student in each subject
    estudiantes.forEach((estudiante, materias){
      print('\n=== ESTUDIANTE: $estudiante ===');
      Map<String, double> promedios = {};
      promedioMateria(materias, promedios);
      imprimirPromedios(promedios);
    });
  }
  ```
</Accordion>

## Best Practices

<CardGroup cols={2}>
  <Card title="Type Safety" icon="shield">
    Always specify types: `Map<String, List<int>>`
  </Card>

  <Card title="Immutability" icon="lock">
    Consider using final for maps that won't be reassigned
  </Card>

  <Card title="Null Safety" icon="circle-exclamation">
    Check for empty collections before operations
  </Card>

  <Card title="Clear Naming" icon="tag">
    Use descriptive names like `calificacionesPorMateria`
  </Card>
</CardGroup>
