Files
JR-priprava-na-skusku/priprava/rust_priprava7.md
2026-02-27 17:59:06 +01:00

15 KiB

itertools — krok za krokom

Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej.

use itertools::Itertools; // toto treba vždy

Cargo.toml:

[dependencies]
itertools = "0.14"

itertools pridáva metódy na každý iterátor. Nič špeciálne netreba robiť — len use itertools::Itertools a všetky metódy sú k dispozícii na .iter(), .into_iter() atď.


Kapitola 1: sorted — zoradenie iterátora

Štandardný Rust nemá .sorted() na iterátoroch. itertools ho pridáva.

use itertools::Itertools;

let cisla = vec![3, 1, 4, 1, 5, 9, 2, 6];

// sorted() — zoradí vzostupne, vráti nový iterátor
let zoradene: Vec<i32> = cisla.iter().copied().sorted().collect();
// [1, 1, 2, 3, 4, 5, 6, 9]

// sorted_by — vlastné porovnanie
let zostupne: Vec<i32> = cisla.iter().copied()
    .sorted_by(|a, b| b.cmp(a))
    .collect();
// [9, 6, 5, 4, 3, 2, 1, 1]

// sorted_by_key — zoraď podľa kľúča
let slova = vec!["banán", "auto", "citrón"];
let podla_dlzky: Vec<&str> = slova.into_iter()
    .sorted_by_key(|s| s.len())
    .collect();
// ["auto", "banán", "citrón"]

So štruktúrami:

struct Student { meno: String, priemer: f32 }

let studenti = vec![
    Student { meno: "Cyril".into(), priemer: 1.5 },
    Student { meno: "Anna".into(), priemer: 2.1 },
    Student { meno: "Boris".into(), priemer: 1.2 },
];

// Zoraď podľa priemeru
let zoradeni: Vec<&Student> = studenti.iter()
    .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
    .collect();
// Boris (1.2), Cyril (1.5), Anna (2.1)

// Zoraď podľa mena abecedne
let abecedne: Vec<&Student> = studenti.iter()
    .sorted_by_key(|s| &s.meno)
    .collect();
// Anna, Boris, Cyril

Bez itertools by si musel klonovať do Vec a volať .sort():

// Bez itertools — zbytočne komplikované
let mut kopie = studenti.clone();
kopie.sort_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap());

// S itertools — jednoduché reťazenie
let vysledok: Vec<&Student> = studenti.iter()
    .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
    .collect();

Úlohy 1

1a. Máš vektor mien. Zoraď ich abecedne pomocou .sorted(). Vypíš.

1b. Máš vektor čísel. Zoraď zostupne pomocou .sorted_by().

1c. Máš vektor reťazcov. Zoraď podľa dĺžky (najkratšie prvé) pomocou .sorted_by_key().

1d. Máš Vec<(String, u32)> — mená a veky. Zoraď podľa veku zostupne.


Kapitola 2: unique — odstránenie duplikátov

use itertools::Itertools;

let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5];

// unique() — odstráni duplikáty, zachová prvý výskyt
let unikatne: Vec<&i32> = cisla.iter().unique().collect();
// [&1, &3, &2, &4, &5] — poradie prvého výskytu je zachované!

// S owned hodnotami
let unikatne: Vec<i32> = cisla.into_iter().unique().collect();
// [1, 3, 2, 4, 5]

Rozdiel oproti HashSet: unique() zachováva poradie prvého výskytu. HashSet nie.

// unique_by — odstráni duplikáty podľa kľúča
struct Student { meno: String, trieda: String }

let studenti = vec![
    Student { meno: "Anna".into(), trieda: "A".into() },
    Student { meno: "Boris".into(), trieda: "B".into() },
    Student { meno: "Cyril".into(), trieda: "A".into() },
    Student { meno: "Dana".into(), trieda: "B".into() },
];

// Jeden študent z každej triedy (prvý nájdený)
let po_jednom: Vec<&Student> = studenti.iter()
    .unique_by(|s| &s.trieda)
    .collect();
// Anna (trieda A), Boris (trieda B) — Cyril a Dana sú duplikáty tried

Úlohy 2

2a. Máš vektor slov s duplikátmi. Odstráň duplikáty a zachovaj poradie.

2b. Máš vektor (String, i32) — (meno, vek). Niektoré mená sa opakujú. Nechaj len prvý výskyt každého mena. Použi unique_by.

2c. Máš vektor reťazcov. Chceš unikátne slová case-insensitive (t.j. "Ahoj" a "ahoj" sú duplikáty). Použi unique_by s .to_lowercase().


Kapitola 3: join — spojenie do reťazca

use itertools::Itertools;

let slova = vec!["ahoj", "svet", "rust"];

// join — spojí prvky oddeľovačom
let veta: String = slova.iter().join(", ");
// "ahoj, svet, rust"

let cisla = vec![1, 2, 3, 4, 5];
let text: String = cisla.iter().join(" - ");
// "1 - 2 - 3 - 4 - 5"

// Prázdny oddeľovač
let spolu: String = slova.iter().join("");
// "ahojsvetrust"

// Funguje s čímkoľvek čo implementuje Display
let floaty = vec![1.5, 2.7, 3.14];
let text: String = floaty.iter().join(", ");
// "1.5, 2.7, 3.14"

Bez itertools by si to robil cez .collect::<Vec<String>>().join(",") alebo ručne.

Úlohy 3

3a. Máš vektor mien. Spoj ich čiarkou a medzerou do jedného reťazca.

3b. Máš vektor čísel 1 až 10. Spoj ich pomlčkou.

3c. Máš vektor štruktúr Kniha { nazov: String }. Spoj názvy kníh do jedného reťazca oddeleného " | ". Hint: najprv .map() na názvy, potom .join().


Kapitola 4: counts — počítanie výskytov

use itertools::Itertools;

let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];

// counts() — vráti HashMap<T, usize> s počtom výskytov
let pocty = slova.iter().counts();
// {&"ahoj": 3, &"svet": 2, &"rust": 1}

// counts_by — počítaj podľa kľúča
let text = "Hello World";
let pocty = text.chars().counts_by(|c| c.is_uppercase());
// {true: 2, false: 9}  (H a W sú veľké)

Toto nahrádza celý ten entry().or_insert(0) vzor:

// BEZ itertools — 4 riadky
let mut pocty = HashMap::new();
for slovo in &slova {
    *pocty.entry(slovo).or_insert(0) += 1;
}

// S itertools — 1 riadok
let pocty = slova.iter().counts();

Úlohy 4

4a. Máš reťazec. Spočítaj výskyt každého znaku. Použi .chars().counts().

4b. Máš vektor štruktúr Kniha { nazov: String, zaner: String }. Spočítaj koľko kníh je v každom žánri. Použi .map() + .counts().

4c. Máš vektor čísel. Spočítaj koľko je párnych a koľko nepárnych. Použi .counts_by().


Kapitola 5: group_by — zoskupovanie

Dôležité: group_by funguje len na už zoradených dátach! Zoskupí po sebe idúce prvky s rovnakým kľúčom.

use itertools::Itertools;

// Dáta MUSIA byť zoradené podľa rovnakého kľúča!
let cisla = vec![1, 1, 2, 2, 2, 3, 3, 1];

// group_by zoskupí PO SEBE IDÚCE prvky
for (kluc, skupina) in &cisla.iter().group_by(|&&c| c) {
    let prvky: Vec<&&i32> = skupina.collect();
    println!("{}: {:?}", kluc, prvky);
}
// 1: [&&1, &&1]
// 2: [&&2, &&2, &&2]
// 3: [&&3, &&3]
// 1: [&&1]        ← POZOR! Posledná 1 je v NOVEJ skupine!

Ak chceš všetky rovnaké spolu, musíš najprv zoradiť:

let cisla = vec![1, 3, 2, 1, 2, 3, 1];

// Najprv zoradiť, potom group_by
for (kluc, skupina) in &cisla.iter().sorted().group_by(|&&c| c) {
    let pocet = skupina.count();
    println!("{}: {}x", kluc, pocet);
}
// 1: 3x
// 2: 2x
// 3: 2x

So štruktúrami:

struct Kniha { nazov: String, zaner: String }

let knihy = vec![/* ... */];

// Zoskup podľa žánru — musíš najprv zoradiť!
let zoradene: Vec<&Kniha> = knihy.iter()
    .sorted_by_key(|k| &k.zaner)
    .collect();

for (zaner, skupina) in &zoradene.iter().group_by(|k| &k.zaner) {
    let knihy_v_zanri: Vec<&&&Kniha> = skupina.collect();
    println!("{}: {} kníh", zaner, knihy_v_zanri.len());
}

Tip: Pre jednoduché počítanie použi radšej .counts() z kapitoly 4. group_by je užitočný keď chceš s celou skupinou niečo robiť (nie len počítať).

Úlohy 5

5a. Máš vektor slov zoradený abecedne. Zoskup podľa prvého písmena a vypíš koľko slov začína na ktoré písmeno.

5b. Máš vektor čísel. Zoraď ich a potom zoskup rovnaké čísla. Pre každú skupinu vypíš číslo a koľkokrát sa vyskytuje.


Kapitola 6: chunk_by a chunks

chunks — rozdeľ na kúsky pevnej veľkosti

use itertools::Itertools;

let cisla = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// chunks(3) — skupiny po 3
for chunk in &cisla.iter().chunks(3) {
    let skupina: Vec<&i32> = chunk.collect();
    println!("{:?}", skupina);
}
// [1, 2, 3]
// [4, 5, 6]
// [7, 8, 9]
// [10]        ← posledná skupina môže byť menšia

tuple_windows — posuvné okno

let cisla = vec![1, 2, 3, 4, 5];

// Dvojice po sebe idúcich
for (a, b) in cisla.iter().tuple_windows() {
    println!("{} a {}", a, b);
}
// 1 a 2
// 2 a 3
// 3 a 4
// 4 a 5

// Trojice
for (a, b, c) in cisla.iter().tuple_windows() {
    println!("{}, {}, {}", a, b, c);
}
// 1, 2, 3
// 2, 3, 4
// 3, 4, 5

Úlohy 6

6a. Máš vektor 20 študentov. Rozdeľ ich do skupín po 5. Vypíš každú skupinu.

6b. Máš vektor denných teplôt. Vypočítaj priemernú teplotu za každý 7-dňový úsek (chunks(7)).

6c. Máš vektor čísel. Použi tuple_windows na nájdenie všetkých párov po sebe idúcich čísel, kde druhé je väčšie ako prvé (rastúce páry).


Kapitola 7: min_set, max_set — všetky minimá/maximá

Štandardný .min() vráti len jedno minimum. Čo ak je ich viac?

use itertools::Itertools;

let cisla = vec![3, 1, 4, 1, 5, 1, 2];

// min_set — vráti Vec VŠETKÝCH miním
let minima: Vec<&i32> = cisla.iter().min_set();
// [&1, &1, &1] — tri jednotky

// max_set
let maxima: Vec<&i32> = cisla.iter().max_set();
// [&5]

// min_set_by_key — so štruktúrami
struct Student { meno: String, priemer: f32 }
let studenti = vec![/* ... */];

// Všetci študenti s najlepším priemerom
let najlepsi: Vec<&Student> = studenti.iter()
    .min_set_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap());

Úlohy 7

7a. Máš vektor čísel. Nájdi všetky výskyty maximálnej hodnoty. Koľko ich je?

7b. Máš vektor slov. Nájdi všetky najdlhšie slová (môže ich byť viac rovnako dlhých). Použi max_set_by_key.


Kapitola 8: interleave a zip_eq

interleave — striedavo z dvoch iterátorov

use itertools::Itertools;

let a = vec![1, 3, 5];
let b = vec![2, 4, 6];

let striedavo: Vec<i32> = a.into_iter().interleave(b.into_iter()).collect();
// [1, 2, 3, 4, 5, 6]

let x = vec!["a", "b", "c"];
let y = vec!["1", "2", "3"];
let mix: Vec<&str> = x.into_iter().interleave(y.into_iter()).collect();
// ["a", "1", "b", "2", "c", "3"]

zip_eq — zip ktorý panicky ak rôzna dĺžka

Štandardný .zip() ticho skráti na kratší. zip_eq ti povie ak majú rôznu dĺžku:

let mena = vec!["Anna", "Boris", "Cyril"];
let veky = vec![25, 30, 22];

// Štandardný zip — OK
let pary: Vec<_> = mena.iter().zip(veky.iter()).collect();

// zip_eq — rovnaké, ale panikne ak rôzna dĺžka
let pary: Vec<_> = mena.iter().zip_eq(veky.iter()).collect();

// let veky_kratsi = vec![25, 30];
// mena.iter().zip_eq(veky_kratsi.iter()).collect(); // PANIC!

Úlohy 8

8a. Máš vektor otázok a vektor odpovedí. Spáruj ich a vypíš "Otázka: ... → Odpoveď: ...". Použi zip_eq (chceš aby program spadol ak nesedia počty).

8b. Máš dva vektory — párne a nepárne čísla. Prelož ich striedavo do jedného vektora.


Kapitola 9: format_with a positions

positions — indexy prvkov splňajúcich podmienku

use itertools::Itertools;

let cisla = vec![10, 25, 30, 5, 40, 15];

// Indexy prvkov > 20
let indexy: Vec<usize> = cisla.iter().positions(|c| *c > 20).collect();
// [1, 2, 4]  (25 je na indexe 1, 30 na 2, 40 na 4)

Toto je jednoduchšie ako .enumerate().filter().map():

// BEZ itertools — dlhé
let indexy: Vec<usize> = cisla.iter().enumerate()
    .filter(|(_, c)| **c > 20)
    .map(|(i, _)| i)
    .collect();

// S itertools — krátke
let indexy: Vec<usize> = cisla.iter().positions(|c| *c > 20).collect();

Úlohy 9

9a. Máš vektor slov. Nájdi pozície (indexy) všetkých slov dlhších ako 5 znakov.

9b. Máš vektor študentov s priemerom. Nájdi indexy študentov s priemerom pod 1.5.


Kapitola 10: Kombinácie na skúške

Vzor 1: Zoradený výpis štatistík

use itertools::Itertools;

struct Kniha { nazov: String, vydavatelstvo: String }

fn vypis_statistiky(knihy: &[Kniha]) {
    // counts + sorted — zoradený výpis počtov
    let pocty = knihy.iter().map(|k| &k.vydavatelstvo).counts();
    for (vydavatelstvo, pocet) in pocty.iter().sorted_by_key(|(_, p)| std::cmp::Reverse(*p)) {
        println!("{}: {}", vydavatelstvo, pocet);
    }
}

Vzor 2: Unikátne zoradené hodnoty

fn unikatne_zanre_zoradene(knihy: &[Kniha]) -> Vec<String> {
    knihy.iter()
        .map(|k| k.vydavatelstvo.clone())
        .unique()
        .sorted()
        .collect()
}

Vzor 3: Top N

fn top_3_studenti(studenti: &[Student]) -> Vec<&Student> {
    studenti.iter()
        .sorted_by(|a, b| a.priemer.partial_cmp(&b.priemer).unwrap())
        .take(3)
        .collect()
}

Vzor 4: Formátovaný výpis zoznamu

fn vypis_mena(studenti: &[Student]) -> String {
    studenti.iter().map(|s| &s.meno).join(", ")
}
// "Anna, Boris, Cyril"

Vzor 5: Zoskupenie a štatistiky

fn priemer_podla_triedy(studenti: &[Student]) {
    for (trieda, skupina) in &studenti.iter()
        .sorted_by_key(|s| &s.trieda)
        .group_by(|s| &s.trieda)
    {
        let zoznam: Vec<&Student> = skupina.collect();
        let sucet: f32 = zoznam.iter().map(|s| s.priemer).sum();
        let priemer = sucet / zoznam.len() as f32;
        println!("Trieda {}: priemer {:.2}", trieda, priemer);
    }
}

Úlohy 10

10a. Máš vektor Zamestnanec { meno: String, oddelenie: String, plat: u32 }. Napíš funkciu, ktorá:

  1. Zoskupí zamestnancov podľa oddelenia
  2. Pre každé oddelenie vypíše mená zoradené abecedne, spojené čiarkou
  3. Oddelenia vypíše zoradené abecedne

Použi: sorted_by_key, group_by, join.

10b. Máš vektor kníh. Nájdi 5 najčastejších žánrov. Použi counts + sorted_by_key + take.

10c. Máš vektor čísel. Použi tuple_windows na zistenie najväčšieho rozdielu medzi dvoma po sebe idúcimi číslami.

10d. Máš vektor reťazcov s duplikátmi. Vyfiltruj unikátne, zoraď abecedne, spoj do jedného reťazca oddeleného " | ". Všetko jedným reťazením.


Prehľad: kedy čo z itertools

Chcem... Metóda
Zoradiť iterátor .sorted(), .sorted_by(), .sorted_by_key()
Odstrániť duplikáty (so zachovaním poradia) .unique(), .unique_by()
Spojiť do reťazca .join(separator)
Spočítať výskyty → HashMap .counts(), .counts_by()
Zoskupiť po sebe idúce .group_by() (najprv sorted!)
Rozdeliť na kúsky .chunks(n)
Posuvné okno .tuple_windows()
Všetky minimá/maximá .min_set(), .max_set()
Preložiť dva iterátory .interleave()
Zip s kontrolou dĺžky .zip_eq()
Indexy splňajúce podmienku .positions()