Files
JR-priprava-na-skusku/priprava/rust_priprava4.md
2026-02-22 18:45:04 +01:00

18 KiB

HashMap — krok za krokom

Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. Riešenia sú na konci, číslované.

use std::collections::HashMap; // toto treba vždy

Kapitola 1: Vytvorenie a insert

HashMap je kolekcia párov kľúč → hodnota. Kľúč je unikátny.

let mut mapa: HashMap<String, i32> = HashMap::new();

mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// mapa = {"Anna": 25, "Boris": 30}

// insert na existujúci kľúč PREPÍŠE hodnotu a vráti starú
let stara = mapa.insert("Anna".to_string(), 26);
// stara = Some(25), mapa = {"Anna": 26, "Boris": 30}

// insert na nový kľúč vráti None
let stara = mapa.insert("Cyril".to_string(), 22);
// stara = None

Úlohy 1

1a. Vytvor HashMap kde kľúče sú mená ovocia (String) a hodnoty sú ceny (f64). Vlož 3 ovocia.

1b. Čo vráti posledný insert? Napíš kód a over:

let mut m = HashMap::new();
m.insert("x", 1);
m.insert("y", 2);
let vysledok = m.insert("x", 99);
println!("{:?}", vysledok);

1c. Vytvor HashMap z vektora dvojíc pomocou .collect():

let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
// tvoj kód → HashMap<&str, &str>

Kapitola 2: Čítanie — get, contains_key, len

let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);

// get — vráti Option<&V>
let vek = mapa.get("Anna");     // Some(&25)
let vek = mapa.get("Cyril");    // None

// contains_key — vráti bool
let je_tam = mapa.contains_key("Anna");   // true
let je_tam = mapa.contains_key("Cyril");  // false

// len — počet párov
let pocet = mapa.len();  // 2

// is_empty
let prazdna = mapa.is_empty();  // false

Dôležité: Nikdy nepoužívaj mapa["Anna"] — ak kľúč neexistuje, program spadne. Vždy .get().

Úlohy 2

2a. Máš mapu ovocia z úlohy 1a. Napíš kód, ktorý:

  • Vypíše cenu jablka ak existuje
  • Vypíše "Nemáme" ak neexistuje Použi if let s .get().

2b. Napíš funkciu:

fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool

Použi contains_key.

2c. Čo vypíše? Tipni, potom over:

let mut m: HashMap<&str, i32> = HashMap::new();
m.insert("a", 1);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));
println!("{}", m.len());
println!("{}", m.contains_key("a"));
println!("{}", m.contains_key("b"));

Kapitola 3: Mazanie — remove, clear

let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);

// remove — vráti Option<V> (hodnotu, nie referenciu)
let odstraneny = mapa.remove("Anna");
// odstraneny = Some(25), mapa = {"Boris": 30}

let odstraneny = mapa.remove("Cyril");
// odstraneny = None (neexistoval)

// clear — vymaže všetko
mapa.clear();
// mapa = {}

Úlohy 3

3a. Vytvor mapu s 3 položkami. Odstráň jednu. Vypíš čo remove vrátil. Vypíš veľkosť mapy.

3b. Napíš funkciu:

fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>

Ak kľúč existuje, odstráň ho a vráť hodnotu. Ak nie, vráť None. (Hint: remove presne toto robí.)


Kapitola 4: Iterácia

let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 22);

// Cez páry — poradie NIE JE garantované!
for (meno, vek) in &mapa {
    println!("{}: {}", meno, vek);
}

// Len kľúče
for meno in mapa.keys() {
    println!("{}", meno);
}

// Len hodnoty
for vek in mapa.values() {
    println!("{}", vek);
}

// Meniteľné hodnoty
for vek in mapa.values_mut() {
    *vek += 1;
}

Úlohy 4

4a. Vytvor mapu produktov (názov → cena). Vypíš všetky produkty vo formáte "Produkt: cena €".

4b. Napíš funkciu, ktorá vráti vektor všetkých kľúčov z mapy:

fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String>

4c. Napíš funkciu, ktorá zvýši všetky hodnoty v mape o 10:

fn zvys_vsetky(mapa: &mut HashMap<String, i32>)

4d. Napíš funkciu, ktorá vráti súčet všetkých hodnôt:

fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32

Použi .values() a .sum().


Kapitola 5: get_mut — zmena hodnoty na mieste

Ak chceš zmeniť jednu konkrétnu hodnotu (nie všetky), použi get_mut:

let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);

// get_mut vráti Option<&mut V>
if let Some(vek) = mapa.get_mut("Anna") {
    *vek += 1;  // Anna má teraz 26
}

// Ak kľúč neexistuje, get_mut vráti None
if let Some(vek) = mapa.get_mut("Boris") {
    *vek += 1;  // Toto sa nevykoná
}

Úlohy 5

5a. Máš mapu študentov (meno → počet bodov). Napíš funkciu, ktorá pridá body jednému študentovi:

fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool

Vráti true ak študent existoval, false ak nie.

5b. Napíš funkciu, ktorá zdvojnásobí hodnotu pre daný kľúč:

fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>

Vráti novú hodnotu ak kľúč existoval, None ak nie.


Kapitola 6: Entry API — or_insert

Toto je najdôležitejšia časť. Entry API rieši situáciu: "ak kľúč neexistuje, vlož default; ak existuje, použi existujúcu hodnotu."

let mut mapa: HashMap<String, i32> = HashMap::new();

// entry() vráti Entry — buď Vacant (prázdny) alebo Occupied (obsadený)
// or_insert(hodnota) — ak Vacant, vlož hodnotu. Vráti &mut na hodnotu.

mapa.entry("Anna".to_string()).or_insert(25);
// Anna neexistovala → vloží 25 → mapa = {"Anna": 25}

mapa.entry("Anna".to_string()).or_insert(99);
// Anna existuje → NEVLOŽÍ 99 → mapa = {"Anna": 25} (nezmenené!)

Hlavné použitie — počítanie výskytov:

let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"];
let mut pocty: HashMap<String, usize> = HashMap::new();

for slovo in &slova {
    *pocty.entry(slovo.to_string()).or_insert(0) += 1;
}
// {"ahoj": 3, "svet": 2, "rust": 1}

Čo sa deje krok po kroku:

Slovo "ahoj":
  entry("ahoj") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
  *(&mut 0) += 1 → hodnota je 1

Slovo "svet":
  entry("svet") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
  *(&mut 0) += 1 → hodnota je 1

Slovo "ahoj" (druhýkrát):
  entry("ahoj") → Occupied (hodnota 1) → or_insert(0) NEVLOŽÍ → vráti &mut 1
  *(&mut 1) += 1 → hodnota je 2

Úlohy 6

6a. Spočítaj výskyt každého znaku v reťazci "abrakadabra". Použi entry + or_insert. Výsledok vypíš.

6b. Máš vektor čísel [1, 3, 2, 1, 4, 3, 2, 1, 5]. Spočítaj koľkokrát sa vyskytuje každé číslo.

6c. Čo vypíše? Tipni, potom over:

let mut m: HashMap<&str, i32> = HashMap::new();
m.entry("a").or_insert(10);
m.entry("b").or_insert(20);
m.entry("a").or_insert(99);
m.insert("b", 99);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));

Kapitola 7: Entry API — or_default

or_default() je skratka za or_insert(Default::default()). Vloží defaultnú hodnotu pre daný typ.

Typ Default
usize, i32, u32... 0
f64 0.0
bool false
String ""
Vec vec![]
let mut pocty: HashMap<String, usize> = HashMap::new();

// Tieto dva riadky robia to isté:
*pocty.entry("ahoj".to_string()).or_insert(0) += 1;
*pocty.entry("ahoj".to_string()).or_default() += 1;

Hlavné použitie — zoskupovanie do vektorov:

let mut skupiny: HashMap<String, Vec<String>> = HashMap::new();

// or_default() pre Vec vloží prázdny vektor
skupiny.entry("ovocie".to_string()).or_default().push("jablko".to_string());
skupiny.entry("ovocie".to_string()).or_default().push("hruška".to_string());
skupiny.entry("zelenina".to_string()).or_default().push("mrkva".to_string());
// {"ovocie": ["jablko", "hruška"], "zelenina": ["mrkva"]}

Úlohy 7

7a. Máš vektor slov. Zoskup ich podľa prvého písmena do HashMap<char, Vec>:

let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
// Výsledok: {'a': ["auto", "ahoj", "auto"], 'b': ["banán", "breza"], 'c': ["citrón"]}

7b. Máš vektor dvojíc (študent, predmet). Zoskup predmety podľa študenta:

let data = vec![
    ("Anna", "Matematika"),
    ("Boris", "Fyzika"),
    ("Anna", "Fyzika"),
    ("Boris", "Chémia"),
    ("Anna", "Chémia"),
];
// Výsledok: {"Anna": ["Matematika", "Fyzika", "Chémia"], "Boris": ["Fyzika", "Chémia"]}

Kapitola 8: Entry API — and_modify

and_modify ti umožní spraviť niečo s existujúcou hodnotou pred tým, ako sa rozhodne čo robiť:

let mut mapa: HashMap<String, i32> = HashMap::new();

// Ak existuje → zvýš o 1. Ak neexistuje → vlož 1.
mapa.entry("Anna".to_string())
    .and_modify(|v| *v += 1)
    .or_insert(1);
// Anna neexistovala → vloží 1

mapa.entry("Anna".to_string())
    .and_modify(|v| *v += 1)
    .or_insert(1);
// Anna existuje s hodnotou 1 → and_modify zvýši na 2

Toto je užitočné keď chceš robiť rôzne veci pre nový vs existujúci kľúč.

Úlohy 8

8a. Máš vektor mien. Pre každé meno: ak sa vyskytlo prvýkrát, vlož 1. Ak sa vyskytlo znovu, zvýš o 1. Použi and_modify + or_insert. (Áno, výsledok je rovnaký ako cez or_insert(0) += 1, ale precvič si syntax.)

8b. Máš mapu produktov (String → (cena: f64, počet: u32)). Napíš kód, ktorý: ak produkt existuje, zvýši počet o 1. Ak neexistuje, vloží ho s cenou 10.0 a počtom 1.

let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
// tvoj kód pre produkt "mlieko"

Kapitola 9: Iterátory s HashMap

HashMap sa dá kombinovať s iterátorovými metódami:

let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 17);

// filter — nechaj len dospelých
let dospeli: HashMap<&String, &i32> = mapa.iter()
    .filter(|(_, vek)| **vek >= 18)
    .collect();

// find — nájdi prvého s daným menom
let boris: Option<(&String, &i32)> = mapa.iter()
    .find(|(meno, _)| meno.as_str() == "Boris");

// max_by_key — najstarší
let najstarsi: Option<(&String, &i32)> = mapa.iter()
    .max_by_key(|(_, vek)| *vek);

// Kľúče do Vec
let mena: Vec<&String> = mapa.keys().collect();

// Hodnoty — súčet
let sucet_vekov: i32 = mapa.values().sum();

Úlohy 9

9a. Máš HashMap<String, f64> (produkt → cena). Nájdi najdrahší produkt. Vráť jeho názov ako Option<&String>.

9b. Máš HashMap<String, u32> (mesto → populácia). Vráť Vec miest s populáciou nad 100_000.

9c. Máš HashMap<String, Vec<String>> (kategória → produkty). Nájdi celkový počet produktov naprieč všetkými kategóriami. Použi .values() a .map() a .sum().


Kapitola 10: Praktické vzory zo skúšky

Vzor 1: Počítanie podľa poľa štruktúry

struct Kniha { nazov: String, vydavatelstvo: String }

fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap<String, usize> {
    let mut mapa = HashMap::new();
    for kniha in knihy {
        *mapa.entry(kniha.vydavatelstvo.clone()).or_insert(0) += 1;
    }
    mapa
}

Vzor 2: Výpis vo formáte "Kľúč: hodnota"

fn vypis_statistiky(knihy: &[Kniha]) {
    let pocty = pocty_podla_vydavatelstva(knihy);
    for (vydavatelstvo, pocet) in &pocty {
        println!("{}: {}", vydavatelstvo, pocet);
    }
}

Vzor 3: Nájdi najčastejší

fn najcastejsie_vydavatelstvo(knihy: &[Kniha]) -> Option<String> {
    let pocty = pocty_podla_vydavatelstva(knihy);
    pocty.into_iter()
        .max_by_key(|(_, pocet)| *pocet)
        .map(|(vydavatelstvo, _)| vydavatelstvo)
}

Úlohy 10

10a. Máš:

struct Zamestnanec { meno: String, oddelenie: String, plat: u32 }

Napíš funkciu pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize>.

10b. Napíš funkciu celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> — súčet platov na každom oddelení.

10c. Napíš funkciu vypis_statistiky(zamestnanci: &[Zamestnanec]) — pre každé oddelenie vypíše počet ľudí a celkový plat.

10d. Napíš funkciu oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> — oddelenie kde je najväčší celkový plat.




RIEŠENIA

1a

let mut ovocie: HashMap<String, f64> = HashMap::new();
ovocie.insert("Jablko".to_string(), 1.50);
ovocie.insert("Banán".to_string(), 2.00);
ovocie.insert("Pomaranč".to_string(), 2.50);

1b

Some(1)

Lebo "x" už existoval s hodnotou 1, insert ho prepísal na 99 a vrátil starú hodnotu.

1c

let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
let mapa: HashMap<&str, &str> = data.into_iter().collect();

2a

if let Some(cena) = ovocie.get("Jablko") {
    println!("Cena: {} €", cena);
} else {
    println!("Nemáme");
}

2b

fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool {
    mapa.contains_key(kluc)
}

2c

Some(1)
None
1
true
false

3a

let mut mapa = HashMap::new();
mapa.insert("A".to_string(), 1);
mapa.insert("B".to_string(), 2);
mapa.insert("C".to_string(), 3);

let odstraneny = mapa.remove("B");
println!("{:?}", odstraneny);  // Some(2)
println!("{}", mapa.len());    // 2

3b

fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
    mapa.remove(kluc)
}

4a

let mut produkty = HashMap::new();
produkty.insert("Chlieb".to_string(), 1.20);
produkty.insert("Mlieko".to_string(), 0.89);
produkty.insert("Maslo".to_string(), 2.50);

for (nazov, cena) in &produkty {
    println!("{}: {} €", nazov, cena);
}

4b

fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String> {
    mapa.keys().collect()
}

4c

fn zvys_vsetky(mapa: &mut HashMap<String, i32>) {
    for hodnota in mapa.values_mut() {
        *hodnota += 10;
    }
}

4d

fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32 {
    mapa.values().sum()
}

5a

fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool {
    if let Some(aktualne) = mapa.get_mut(meno) {
        *aktualne += body;
        true
    } else {
        false
    }
}

5b

fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
    let hodnota = mapa.get_mut(kluc)?;
    *hodnota *= 2;
    Some(*hodnota)
}

6a

let text = "abrakadabra";
let mut pocty: HashMap<char, usize> = HashMap::new();
for c in text.chars() {
    *pocty.entry(c).or_insert(0) += 1;
}
for (znak, pocet) in &pocty {
    println!("{}: {}", znak, pocet);
}

6b

let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5];
let mut pocty: HashMap<i32, usize> = HashMap::new();
for c in &cisla {
    *pocty.entry(*c).or_insert(0) += 1;
}
// {1: 3, 3: 2, 2: 2, 4: 1, 5: 1}

6c

Some(10)
Some(99)

entry("a").or_insert(99) — "a" už existuje, takže 99 sa NEVLOŽÍ, ostáva 10. insert("b", 99) — insert VŽDY prepíše, takže "b" je teraz 99.

7a

let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
let mut skupiny: HashMap<char, Vec<String>> = HashMap::new();
for slovo in &slova {
    if let Some(prvy) = slovo.chars().next() {
        skupiny.entry(prvy).or_default().push(slovo.to_string());
    }
}

7b

let data = vec![
    ("Anna", "Matematika"),
    ("Boris", "Fyzika"),
    ("Anna", "Fyzika"),
    ("Boris", "Chémia"),
    ("Anna", "Chémia"),
];
let mut mapa: HashMap<String, Vec<String>> = HashMap::new();
for (student, predmet) in &data {
    mapa.entry(student.to_string()).or_default().push(predmet.to_string());
}

8a

let mena = vec!["Anna", "Boris", "Anna", "Cyril", "Boris", "Anna"];
let mut pocty: HashMap<String, i32> = HashMap::new();
for meno in &mena {
    pocty.entry(meno.to_string())
        .and_modify(|v| *v += 1)
        .or_insert(1);
}
// {"Anna": 3, "Boris": 2, "Cyril": 1}

8b

let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
produkty.entry("mlieko".to_string())
    .and_modify(|(_, pocet)| *pocet += 1)
    .or_insert((10.0, 1));

9a

fn najdrahsi(mapa: &HashMap<String, f64>) -> Option<&String> {
    mapa.iter()
        .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
        .map(|(nazov, _)| nazov)
}

9b

fn velke_mesta(mapa: &HashMap<String, u32>) -> Vec<&String> {
    mapa.iter()
        .filter(|(_, pop)| **pop > 100_000)
        .map(|(mesto, _)| mesto)
        .collect()
}

9c

fn celkovy_pocet(mapa: &HashMap<String, Vec<String>>) -> usize {
    mapa.values().map(|v| v.len()).sum()
}

10a

fn pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize> {
    let mut mapa = HashMap::new();
    for z in zamestnanci {
        *mapa.entry(z.oddelenie.clone()).or_insert(0) += 1;
    }
    mapa
}

10b

fn celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> {
    let mut mapa: HashMap<String, u32> = HashMap::new();
    for z in zamestnanci {
        *mapa.entry(z.oddelenie.clone()).or_insert(0) += z.plat;
    }
    mapa
}

10c

fn vypis_statistiky(zamestnanci: &[Zamestnanec]) {
    let pocty = pocet_na_oddeleni(zamestnanci);
    let platy = celkovy_plat_na_oddeleni(zamestnanci);
    for (oddelenie, pocet) in &pocty {
        let plat = platy.get(oddelenie).unwrap_or(&0);
        println!("{}: {} ľudí, celkový plat: {}", oddelenie, pocet, plat);
    }
}

10d

fn oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> {
    let platy = celkovy_plat_na_oddeleni(zamestnanci);
    platy.into_iter()
        .max_by_key(|(_, plat)| *plat)
        .map(|(oddelenie, _)| oddelenie)
}