Vec::sort_by în Rust

Sortează după o regulă proprie: sort_by primește o funcție de comparare, sort_by_key o cheie calculată din element.

Sintaxă
pub fn sort_by<F>(&mut self, compare: F)
where F: FnMut(&T, &T) -> Ordering

pub fn sort_by_key<K, F>(&mut self, f: F)   // Rust 1.7
where F: FnMut(&T) -> K, K: Ord

Ce face Vec::sort_by

sort_by_key(|e| cheie) este varianta cea mai simplă: spui după ce să se sorteze (vârsta, prețul, lungimea numelui), iar elementele se ordonează crescător după acea cheie. Cheia trebuie să implementeze Ord.

sort_by(|a, b| ...) primește două elemente și returnează un Ordering (Less, Equal, Greater), de obicei obținut cu a.cmp(b). Inversând (b.cmp(a)) obții ordine descrescătoare. Pentru mai multe criterii, înlănțuiește cu .then_with(|| ...), iar pentru f64 folosește total_cmp.

Ambele sunt stabile: la chei egale, elementele își păstrează ordinea anterioară. Variantele sort_unstable_by și sort_unstable_by_key sunt mai rapide, fără această garanție. Cheia lui sort_by_key nu poate fi o referință în element; dacă e scump de calculat (de exemplu un String nou), sort_by_cached_key o calculează o singură dată pe element.

Parametri

Parametru Descriere
compare La sort_by: closure care compară două elemente și returnează Ordering.
f La sort_by_key: closure care calculează cheia de sortare a unui element.

Ce returnează

Nimic; vectorul este sortat pe loc.

Exemple

Exemplul 1: Elevi după vârstă, apoi după medie

Mihai și Radu au aceeași medie (8,70). După a doua sortare, Radu rămâne înaintea lui Mihai, cum era după sortarea pe vârstă: sortarea este stabilă.

struct Elev {
    nume: &'static str,
    varsta: u32,
    medie: f64,
}

fn afiseaza(titlu: &str, elevi: &[Elev]) {
    println!("{titlu}:");
    for e in elevi {
        println!("  {:6} {} ani, media {:.2}", e.nume, e.varsta, e.medie);
    }
}

fn main() {
    let mut elevi = vec![
        Elev { nume: "Ana", varsta: 17, medie: 9.45 },
        Elev { nume: "Mihai", varsta: 16, medie: 8.70 },
        Elev { nume: "Ioana", varsta: 17, medie: 9.80 },
        Elev { nume: "Radu", varsta: 15, medie: 8.70 },
    ];
    elevi.sort_by_key(|e| e.varsta);
    afiseaza("după vârstă", &elevi);

    elevi.sort_by(|a, b| b.medie.total_cmp(&a.medie));
    afiseaza("după medie, descrescător", &elevi);
}

Rezultat

după vârstă:
  Radu   15 ani, media 8.70
  Mihai  16 ani, media 8.70
  Ana    17 ani, media 9.45
  Ioana  17 ani, media 9.80
după medie, descrescător:
  Ioana  17 ani, media 9.80
  Ana    17 ani, media 9.45
  Radu   15 ani, media 8.70
  Mihai  16 ani, media 8.70

Exemplul 2: Două criterii: preț descrescător, apoi nume

fn main() {
    let mut produse = vec![("lapte", 8), ("cafea", 25), ("pâine", 5), ("unt", 12), ("ouă", 12), ("miere", 25)];
    produse.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(b.0)));
    for (nume, pret) in &produse {
        println!("{pret:3} lei  {nume}");
    }
}

Rezultat

 25 lei  cafea
 25 lei  miere
 12 lei  ouă
 12 lei  unt
  8 lei  lapte
  5 lei  pâine

Exemplul 3: Sortare fără să conteze literele mari

Sortarea obișnuită pune literele mari înaintea celor mici. Cu cheia to_lowercase(), ordinea devine cea așteptată.

fn main() {
    let mut nume = vec!["mihai", "Ana", "ioana", "Radu", "Bogdan"];
    nume.sort();
    println!("sort():                 {:?}", nume);
    nume.sort_by_key(|n| n.to_lowercase());
    println!("sort_by_key(lowercase): {:?}", nume);
}

Rezultat

sort():                 ["Ana", "Bogdan", "Radu", "ioana", "mihai"]
sort_by_key(lowercase): ["Ana", "Bogdan", "ioana", "mihai", "Radu"]

Greșeli frecvente

  • Returnezi o referință din cheie: v.sort_by_key(|e| &e.nume) dă o eroare de durată de viață (lifetime may not live long enough). Folosește v.sort_by(|a, b| a.nume.cmp(&b.nume)) sau clonează cheia.
  • Folosești partial_cmp(..).unwrap() pentru f64: dacă apare un NaN, programul se oprește. total_cmp nu are această problemă.
  • Sortezi descrescător cu sort() urmat de reverse() pe date cu chei egale: ordinea elementelor egale se inversează și ea. sort_by(|a, b| b.cmp(a)) sau sort_by_key(|x| Reverse(x)) păstrează stabilitatea.

Comenzi înrudite

  • Vec::sort: Sortează vectorul crescător, pe loc. sort este stabilă (păstrează ordinea elementelor egale); sort_unstable e mai rapidă.
  • Reverse: Învelitoare care inversează ordinea unei valori: transformă BinaryHeap în min-heap și sortează descrescător după cheie.
  • Vec::binary_search: Caută rapid o valoare într-un vector sortat: Ok(poziție) dacă există, Err(poziție) unde ar trebui inserată.
  • Iterator::min_by_key: Găsește elementul minim sau maxim după un criteriu: o cheie calculată (by_key) sau o funcție de comparare (by).
  • Vec::reverse: Inversează pe loc ordinea elementelor: primul devine ultimul, ultimul devine primul.