Iterator::min_by_key în Rust

Găsește elementul minim sau maxim după un criteriu: o cheie calculată (by_key) sau o funcție de comparare (by).

Sintaxă
fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
where F: FnMut(&Self::Item) -> B

fn min_by<F>(self, compare: F) -> Option<Self::Item>   // Rust 1.15
fn max_by<F>(self, compare: F) -> Option<Self::Item>
where F: FnMut(&Self::Item, &Self::Item) -> Ordering

Ce face Iterator::min_by_key

max_by_key(|e| cheie) returnează elementul pentru care cheia este cea mai mare: produsul cel mai scump, elevul cel mai mare, cuvântul cel mai lung. min_by_key face invers. Cheia trebuie să implementeze Ord, deci nu poate fi f64.

max_by(|a, b| ...) și min_by primesc o funcție de comparare care returnează Ordering. Sunt necesare pentru f64 (a.total_cmp(b)) sau pentru criterii compuse (a.x.cmp(&b.x).then(...)).

La egalitate, variantele max returnează ultimul element maxim, iar variantele min primul element minim. Pentru un iterator gol, rezultatul este None.

Parametri

Parametru Descriere
f La *_by_key: closure care calculează cheia unui element (primit ca &).
compare La *_by: closure care compară două elemente și returnează Ordering.

Ce returnează

Option<Self::Item>: elementul întreg, nu cheia.

Exemple

Exemplul 1: Cel mai tânăr și cel mai în vârstă

fn main() {
    let elevi = [("Ana", 17), ("Mihai", 15), ("Ioana", 18), ("Radu", 16)];
    let tanar = elevi.iter().min_by_key(|(_, varsta)| *varsta);
    let mare = elevi.iter().max_by_key(|(_, varsta)| *varsta);
    println!("cel mai tânăr: {:?}", tanar);
    println!("cel mai mare: {:?}", mare);

    let cuvinte = ["mere", "portocale", "kiwi"];
    println!("cel mai lung: {:?}", cuvinte.iter().max_by_key(|c| c.chars().count()));
}

Rezultat

cel mai tânăr: Some(("Mihai", 15))
cel mai mare: Some(("Ioana", 18))
cel mai lung: Some("portocale")

Exemplul 2: Produsul cel mai ieftin, cu prețuri f64

fn main() {
    let produse: [(&str, f64); 4] = [("lapte", 8.49), ("pâine", 4.99), ("cafea", 24.5), ("ouă", 14.9)];
    let ieftin = produse.iter().min_by(|a, b| a.1.total_cmp(&b.1));
    let scump = produse.iter().max_by(|a, b| a.1.total_cmp(&b.1));
    if let (Some(i), Some(s)) = (ieftin, scump) {
        println!("cel mai ieftin: {} ({:.2} lei)", i.0, i.1);
        println!("cel mai scump: {} ({:.2} lei)", s.0, s.1);
    }
}

Rezultat

cel mai ieftin: pâine (4.99 lei)
cel mai scump: cafea (24.50 lei)

Exemplul 3: Egalitate: primul minim, ultimul maxim

Ana și Mihai au amândoi 95 de puncte; max_by_key îl dă pe ultimul, Mihai. Radu și Elena au 70; min_by_key o dă pe prima, Elena.

fn main() {
    let puncte = [("Ana", 95), ("Elena", 70), ("Mihai", 95), ("Radu", 70)];
    println!("max: {:?}", puncte.iter().max_by_key(|p| p.1));
    println!("min: {:?}", puncte.iter().min_by_key(|p| p.1));
}

Rezultat

max: Some(("Mihai", 95))
min: Some(("Elena", 70))

Greșeli frecvente

  • Folosești o cheie f64 în max_by_key: the trait bound `f64: Ord` is not satisfied. Folosește max_by(|a, b| a.x.total_cmp(&b.x)).
  • Te aștepți să primești cheia (de exemplu vârsta): primești elementul întreg. Extrage câmpul după aceea.
  • Contezi pe un anumit element la egalitate fără să știi regula: max_* dă ultimul, min_* dă primul.

Comenzi înrudite

  • Iterator::min: Returnează cel mai mic (min) sau cel mai mare (max) element, ca Option: None pentru un iterator gol.
  • Vec::sort_by: Sortează după o regulă proprie: sort_by primește o funcție de comparare, sort_by_key o cheie calculată din element.
  • Iterator::reduce: Combină elementele două câte două, folosind primul element ca valoare inițială; returnează None pentru un iterator gol.
  • HashMap::keys: keys parcurge doar cheile, values doar valorile, iar values_mut dă valorile ca referințe mutabile, pentru modificare.