Iterator::filter în Rust

Păstrează doar elementele pentru care condiția dată returnează true; restul sunt sărite.

Sintaxă
fn filter<P>(self, predicate: P) -> Filter<Self, P>
where P: FnMut(&Self::Item) -> bool

Ce face Iterator::filter

filter(|x| condiție) produce doar elementele care îndeplinesc condiția, în ordinea inițială. Elementele nu sunt modificate. Ca toate adaptoarele, este leneș: nu face nimic până când iteratorul nu este consumat (de for, collect, sum, count etc.).

Closure-ul primește o referință la element (&Self::Item), ca să poată doar să se uite la el. Pentru v.iter(), elementul este deja &T, deci closure-ul primește &&T. De aici modelele des întâlnite: |&&x| x > 5 sau |x| **x > 5. Cu .copied() înainte, lucrezi cu &T simplu.

Parametri

Parametru Descriere
predicate Closure-ul care primește &element și returnează true pentru elementele păstrate.

Ce returnează

Un iterator Filter cu elementele care trec condiția.

Exemple

Exemplul 1: Note de trecere și orașe lungi

fn main() {
    let note = vec![4, 9, 3, 10, 7, 5];
    let trecere: Vec<&i32> = note.iter().filter(|&&n| n >= 5).collect();
    println!("{:?}", trecere);

    let peste_8: Vec<i32> = note.iter().copied().filter(|n| *n > 8).collect();
    println!("{:?}", peste_8);

    let orase = ["Iași", "Constanța", "Arad", "Timișoara"];
    let lungi: Vec<&str> = orase.into_iter().filter(|o| o.chars().count() > 5).collect();
    println!("{:?}", lungi);
}

Rezultat

[9, 10, 7, 5]
[9, 10]
["Constanța", "Timișoara"]

Exemplul 2: Mai multe filtre înlănțuite

Fiecare filter lucrează doar pe ce a trecut de cel dinainte; totul se face dintr-o singură parcurgere.

struct Comanda {
    oras: &'static str,
    suma: u32,
    platita: bool,
}

fn main() {
    let comenzi = vec![
        Comanda { oras: "Iași", suma: 120, platita: true },
        Comanda { oras: "Cluj", suma: 300, platita: false },
        Comanda { oras: "Iași", suma: 45, platita: true },
        Comanda { oras: "Iași", suma: 210, platita: true },
    ];
    let mari_platite_iasi: Vec<u32> = comenzi
        .iter()
        .filter(|c| c.oras == "Iași")
        .filter(|c| c.platita)
        .filter(|c| c.suma >= 100)
        .map(|c| c.suma)
        .collect();
    println!("{:?}", mari_platite_iasi);
}

Rezultat

[120, 210]

Greșeli frecvente

  • Compari direct |x| x > 5 pe v.iter(): expected `&&{integer}`, found integer. Desfă referințele: |&&x| x > 5, sau |x| **x > 5, sau pune .copied() înainte.
  • Folosești filter urmat de count() doar pentru a verifica dacă există un element: any se oprește la primul găsit.
  • Vrei să ștergi elemente dintr-un vector existent: filter creează un iterator nou. Pe loc, folosește Vec::retain.

Comenzi înrudite

  • Iterator::filter_map: Transformă și filtrează într-un singur pas: păstrează valorile din Some(..) returnate de funcție și sare peste None.
  • Iterator::map: Transformă fiecare valoare a iteratorului cu o funcție și dă un iterator cu rezultatele; nu calculează nimic până la consumare.
  • Vec::retain: Păstrează în vector doar elementele pentru care funcția dată returnează true; restul sunt șterse, în aceeași ordine.
  • Iterator::partition: Împarte elementele în două colecții: cele pentru care condiția e adevărată și cele pentru care e falsă.
  • Iterator::take_while: take_while dă elemente cât timp condiția e adevărată și se oprește la primul care nu o îndeplinește; skip_while sare până atunci.