Advanced Map Structures
Maps can contain complex data types, including lists as values. This allows you to create sophisticated data structures for real-world applications.
Maps with List Values
You can create maps where each key maps to a list of values:
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]
};
The syntax Map<String, List<int>> means each key (String) maps to a list of integers. This is perfect for storing multiple values per category.
Real-World Example: Grade Management System
Let’s build a complete grade management system that:
- Calculates the average for each subject
- Finds the best and worst performing subjects
- Generates a comprehensive report
Calculating Subject Averages
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;
});
}
This function takes two maps: one for input (grades) and one for output (averages). This pattern keeps data organized and functions focused.
Finding the Best Subject
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');
}
Finding the Worst Subject
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');
}
We initialize peorPromedio to 100.0 (the maximum possible grade) to ensure any actual grade will be lower.
Generating Reports
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('');
}
Use toStringAsFixed(2) to format decimal numbers to 2 decimal places for cleaner output.
Complete Working Example
void promedioMateria(Map<String, List<int>> calificacionesPorMateria, Map<String, double> promedios){
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) {
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) {
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) {
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]
Complex Map Structures
| Structure | Type | Use Case | Example |
|---|
| Simple Map | Map<String, int> | Single values | Ages, IDs |
| List Values | Map<String, List<int>> | Multiple values per key | Grades, scores |
| Nested Maps | Map<String, Map<String, int>> | Hierarchical data | Categories with subcategories |
| Dynamic Maps | Map<String, dynamic> | Mixed types | JSON data, API responses |
Common Patterns with Complex Maps
Pattern 1: Aggregate Data
Calculate statistics from lists in maps:
map.forEach((key, values) {
double average = values.reduce((a, b) => a + b) / values.length;
print('$key: $average');
});
Pattern 2: Find Extremes
Find maximum or minimum values:
double maxValue = 0;
String maxKey = '';
map.forEach((key, value) {
if (value > maxValue) {
maxValue = value;
maxKey = key;
}
});
Convert one map structure to another:
Map<String, double> transformed = {};
original.forEach((key, list) {
transformed[key] = calculateSomething(list);
});
Pattern 4: Filter and Report
Generate reports based on conditions:
map.forEach((key, value) {
if (value > threshold) {
print('$key exceeds threshold: $value');
}
});
Real-World Applications
Student Records
Track multiple grades per subject for each student
Sales Data
Store daily sales figures for each product or region
Inventory Management
Track multiple warehouses with stock levels per item
Analytics
Collect and analyze time-series data by category
Advanced Techniques
Using reduce() for Calculations
int suma = calificaciones.reduce((a, b) => a + b);
Sorting Map Entries
var sortedEntries = promedios.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
Creating Maps from Lists
Map<String, int> countMap = {};
for (var item in list) {
countMap[item] = (countMap[item] ?? 0) + 1;
}
When working with large maps containing lists:
- Avoid unnecessary iterations through all data
- Cache calculated values instead of recalculating
- Consider using separate maps for different views of the data
For better organization:
- Separate data processing into small, focused functions
- Use descriptive function names that explain what they do
- Keep data structures and their transformations clear and documented
Best Practices
- Separate concerns: Create different maps for raw data vs. calculated results
- Validate data: Check for empty lists before calculating averages
- Use meaningful names: Choose clear variable names like
calificacionesPorMateria
- Format output: Use
toStringAsFixed() for consistent number formatting
- Modular functions: Break complex operations into smaller, reusable functions
Error Handling
void promedioMateria(Map<String, List<int>> calificacionesPorMateria, Map<String, double> promedios){
calificacionesPorMateria.forEach((materia, calificaciones){
if (calificaciones.isEmpty) {
print('Warning: $materia has no grades');
return;
}
int suma = 0;
for (var calificacion in calificaciones) {
suma += calificacion;
}
double promedio = suma / calificaciones.length;
promedios[materia] = promedio;
});
}
Always check if lists are empty before performing calculations to avoid division by zero or other errors.