Iterator::flat_map în Rust

flatten „turtește” un iterator de colecții într-un singur șir de valori; flat_map aplică întâi o funcție, apoi turtește.

Sintaxă
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where F: FnMut(Self::Item) -> U, U: IntoIterator

fn flatten(self) -> Flatten<Self>   // Rust 1.29
where Self::Item: IntoIterator

Ce face Iterator::flat_map

flatten() primește un iterator ale cărui elemente sunt la rândul lor parcurgibile (vectori, slice-uri, Option-uri, iteratori) și produce elementele interioare, pe rând: din [[1, 2], [3]] obții 1, 2, 3. Pentru Option, None nu produce nimic, iar Some(x) produce x.

flat_map(f) este map(f).flatten(): pentru fiecare element, f returnează o colecție sau un iterator, iar rezultatele se lipesc într-un singur șir. Exemplu tipic: din linii de text, toate cuvintele (lines().flat_map(|l| l.split_whitespace())). Ca toate adaptoarele, este leneș: nu face nimic până când iteratorul nu este consumat (de for, collect, sum, count etc.).

Parametri

Parametru Descriere
f La flat_map: closure-ul care returnează, pentru fiecare element, ceva parcurgibil.

Ce returnează

Un iterator cu elementele interioare, în ordine.

Exemple

Exemplul 1: Toate cuvintele din mai multe linii

fn main() {
    let text = "Ana are mere\nMihai are pere\nIoana nu are nimic";
    let cuvinte: Vec<&str> = text.lines().flat_map(|l| l.split_whitespace()).collect();
    println!("{} cuvinte: {:?}", cuvinte.len(), cuvinte);
}

Rezultat

10 cuvinte: ["Ana", "are", "mere", "Mihai", "are", "pere", "Ioana", "nu", "are", "nimic"]

Exemplul 2: flatten pe vectori și pe Option

fn main() {
    let grupe = vec![vec!["Ana", "Mihai"], vec![], vec!["Ioana", "Radu", "Elena"]];
    let toti: Vec<&str> = grupe.into_iter().flatten().collect();
    println!("{:?}", toti);

    let raspunsuri = [Some(8), None, Some(10), None, Some(6)];
    let date: Vec<i32> = raspunsuri.into_iter().flatten().collect();
    println!("{:?}", date);
}

Rezultat

["Ana", "Mihai", "Ioana", "Radu", "Elena"]
[8, 10, 6]

Exemplul 3: Toate combinațiile meniului

Pentru fiecare fel principal, flat_map produce câte o combinație cu fiecare desert.

fn main() {
    let feluri = ["sarmale", "ciorbă"];
    let deserturi = ["papanași", "clătite", "cozonac"];
    let meniuri: Vec<String> = feluri
        .iter()
        .flat_map(|f| deserturi.iter().map(move |d| format!("{f} + {d}")))
        .collect();
    for m in &meniuri {
        println!("{m}");
    }
}

Rezultat

sarmale + papanași
sarmale + clătite
sarmale + cozonac
ciorbă + papanași
ciorbă + clătite
ciorbă + cozonac

Greșeli frecvente

  • Folosești map când funcția returnează un vector și obții Vec<Vec<T>>. Dacă vrei o singură listă, folosește flat_map.
  • În closure-ul interior folosești o variabilă din cel exterior fără move: closure may outlive the current function, but it borrows `f`. Adaugă move la closure-ul interior.

Comenzi înrudite

  • Iterator::map: Transformă fiecare valoare a iteratorului cu o funcție și dă un iterator cu rezultatele; nu calculează nimic până la consumare.
  • Iterator::chain: Lipește două șiruri de valori: produce întâi toate elementele primului, apoi pe ale celui de-al doilea.
  • Vec::concat: concat lipește o listă de liste (sau de texte) într-una singură; join face la fel, cu un separator între ele.
  • Iterator::filter_map: Transformă și filtrează într-un singur pas: păstrează valorile din Some(..) returnate de funcție și sare peste None.