Desafio Rust
Agrupamento de notas por aluno
Enunciado
Dada uma lista de tuplas (nome, nota), agrupe as notas de cada aluno em um HashMap<String, Vec<i32>> usando a entry API. Depois, para cada aluno, calcule a média e imprima os alunos em ordem alfabética, mostrando as notas e a média com duas casas decimais.
Requisitos
- Usar
entry(nome.to_string()).or_insert_with(Vec::new)para agrupar. - Calcular a média de cada aluno com
iter().sum::<i32>() as f64 / notas.len() as f64. - Ordenar os alunos alfabeticamente antes de imprimir.
- Imprimir no formato
Nome: notas [..] média X.XX.
Código inicial
use std::collections::HashMap;
fn main() {
let registros = vec![
("Ana", 8),
("Bruno", 7),
("Ana", 9),
("Carla", 10),
("Bruno", 6),
("Ana", 7),
];
// TODO: agrupe, calcule médias e imprima em ordem alfabética
}
Saída esperada
Ana: notas [8, 9, 7] média 8.00
Bruno: notas [7, 6] média 6.50
Carla: notas [10] média 10.00
Ver dica
Use or_insert_with(Vec::new) para criar um vetor vazio só quando a chave não existir. Para ordenar, colete map.into_iter() em um Vec e use sort_by_key.
Mostrar solução
use std::collections::HashMap;
fn main() {
// Lista de registros (nome, nota)
let registros = vec![
("Ana", 8),
("Bruno", 7),
("Ana", 9),
("Carla", 10),
("Bruno", 6),
("Ana", 7),
];
// HashMap: nome -> Vec de notas
let mut por_aluno: HashMap<String, Vec<i32>> = HashMap::new();
for (nome, nota) in registros {
// Cria um Vec vazio se o aluno ainda não existe e devolve &mut Vec
por_aluno
.entry(nome.to_string())
.or_insert_with(Vec::new)
.push(nota);
}
// Coleta em Vec para ordenar alfabeticamente
let mut alunos: Vec<(String, Vec<i32>)> = por_aluno.into_iter().collect();
alunos.sort_by(|a, b| a.0.cmp(&b.0));
for (nome, notas) in &alunos {
let soma: i32 = notas.iter().sum();
let media = soma as f64 / notas.len() as f64;
println!("{}: notas {:?} média {:.2}", nome, notas, media);
}
}
Passo a passo
let registros = vec![...]cria umVec<(&str, i32)>com os pares nome/nota.HashMap<String, Vec<i32>>associa cada nome a uma lista de notas. UsamosStringporque o mapa será dono das chaves.- No
for,nome.to_string()cria umaStringnova (o&stroriginal não vive o suficiente para ser chave). .entry(...)busca a entrada;.or_insert_with(Vec::new)insere umVecvazio se a chave não existir e devolve&mut Vec<i32>..push(nota)adiciona a nota ao vetor daquele aluno. Tudo em uma expressão encadeada.por_aluno.into_iter().collect()consome o mapa e gera umVec<(String, Vec<i32>)>.alunos.sort_by(|a, b| a.0.cmp(&b.0))ordena pelo nome (primeiro campo da tupla).- No
forfinal,notas.iter().sum()soma as notas, e a média é calculada comas f64. O{:?}imprime o vetor no formato[8, 9, 7].
Por que funciona
A entry API com or_insert_with é ideal quando o valor padrão é caro de criar (como um Vec): a closure só roda se a chave estiver ausente. O HashMap agrupa eficientemente, e a coleta em Vec permite ordenar, já que o mapa não tem ordem. A média usa conversão explícita para f64 e formatação {:.2}.
Erros comuns
- Usar
or_insert(Vec::new())em vez deor_insert_with(Vec::new): compila, mas cria umVecdesnecessariamente a cada chamada, mesmo quando a chave já existe. - Tentar usar
&strcomo chave semto_string(): o compilador reclama que o empréstimo não vive o suficiente ("borrowed value does not live long enough"). - Esquecer o
as f64na média:soma / notas.len()não compila porquei32eusizenão se dividem diretamente. - Ordenar o
HashMapdiretamente: não é possível; é preciso coletar emVecprimeiro.
Outra forma de resolver
Se a ordem não importasse, poderíamos iterar o mapa diretamente:
for (nome, notas) in &por_aluno {
let media = notas.iter().sum::<i32>() as f64 / notas.len() as f64;
println!("{}: média {:.2}", nome, media);
}
Mais curto, porém sem garantia de ordem alfabética.
Saída esperada
Ana: notas [8, 9, 7] média 8.00
Bruno: notas [7, 6] média 6.50
Carla: notas [10] média 10.00