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

  1. let registros = vec![...] cria um Vec<(&str, i32)> com os pares nome/nota.
  2. HashMap<String, Vec<i32>> associa cada nome a uma lista de notas. Usamos String porque o mapa será dono das chaves.
  3. No for, nome.to_string() cria uma String nova (o &str original não vive o suficiente para ser chave).
  4. .entry(...) busca a entrada; .or_insert_with(Vec::new) insere um Vec vazio se a chave não existir e devolve &mut Vec<i32>.
  5. .push(nota) adiciona a nota ao vetor daquele aluno. Tudo em uma expressão encadeada.
  6. por_aluno.into_iter().collect() consome o mapa e gera um Vec<(String, Vec<i32>)>.
  7. alunos.sort_by(|a, b| a.0.cmp(&b.0)) ordena pelo nome (primeiro campo da tupla).
  8. No for final, notas.iter().sum() soma as notas, e a média é calculada com as 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 de or_insert_with(Vec::new): compila, mas cria um Vec desnecessariamente a cada chamada, mesmo quando a chave já existe.
  • Tentar usar &str como chave sem to_string(): o compilador reclama que o empréstimo não vive o suficiente ("borrowed value does not live long enough").
  • Esquecer o as f64 na média: soma / notas.len() não compila porque i32 e usize não se dividem diretamente.
  • Ordenar o HashMap diretamente: não é possível; é preciso coletar em Vec primeiro.

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