# HashMap — krok za krokom Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. Riešenia sú na konci, číslované. ```rust use std::collections::HashMap; // toto treba vždy ``` --- ## Kapitola 1: Vytvorenie a insert HashMap je kolekcia párov kľúč → hodnota. Kľúč je unikátny. ```rust let mut mapa: HashMap = 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: ```rust 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()`: ```rust let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")]; // tvoj kód → HashMap<&str, &str> ``` --- ## Kapitola 2: Čítanie — get, contains_key, len ```rust 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: ```rust fn je_v_mape(mapa: &HashMap, kluc: &str) -> bool ``` Použi `contains_key`. **2c.** Čo vypíše? Tipni, potom over: ```rust 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 ```rust let mut mapa = HashMap::new(); mapa.insert("Anna".to_string(), 25); mapa.insert("Boris".to_string(), 30); // remove — vráti Option (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: ```rust fn odstran_ak_existuje(mapa: &mut HashMap, kluc: &str) -> Option ``` Ak kľúč existuje, odstráň ho a vráť hodnotu. Ak nie, vráť None. (Hint: `remove` presne toto robí.) --- ## Kapitola 4: Iterácia ```rust 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: ```rust fn kluce(mapa: &HashMap) -> Vec<&String> ``` **4c.** Napíš funkciu, ktorá zvýši všetky hodnoty v mape o 10: ```rust fn zvys_vsetky(mapa: &mut HashMap) ``` **4d.** Napíš funkciu, ktorá vráti súčet všetkých hodnôt: ```rust fn sucet_hodnot(mapa: &HashMap) -> 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`: ```rust 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: ```rust fn pridaj_body(mapa: &mut HashMap, meno: &str, body: u32) -> bool ``` Vráti `true` ak študent existoval, `false` ak nie. **5b.** Napíš funkciu, ktorá zdvojnásobí hodnotu pre daný kľúč: ```rust fn zdvojnasob(mapa: &mut HashMap, kluc: &str) -> Option ``` 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." ```rust let mut mapa: HashMap = 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:** ```rust let slova = vec!["ahoj", "svet", "ahoj", "rust", "svet", "ahoj"]; let mut pocty: HashMap = 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: ```rust 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![] | ```rust let mut pocty: HashMap = 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:** ```rust let mut skupiny: HashMap> = 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>: ```rust 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: ```rust 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ť: ```rust let mut mapa: HashMap = 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. ```rust let mut produkty: HashMap = HashMap::new(); // tvoj kód pre produkt "mlieko" ``` --- ## Kapitola 9: Iterátory s HashMap HashMap sa dá kombinovať s iterátorovými metódami: ```rust 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` (produkt → cena). Nájdi najdrahší produkt. Vráť jeho názov ako `Option<&String>`. **9b.** Máš `HashMap` (mesto → populácia). Vráť Vec miest s populáciou nad 100_000. **9c.** Máš `HashMap>` (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 ```rust struct Kniha { nazov: String, vydavatelstvo: String } fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap { 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" ```rust 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ší ```rust fn najcastejsie_vydavatelstvo(knihy: &[Kniha]) -> Option { let pocty = pocty_podla_vydavatelstva(knihy); pocty.into_iter() .max_by_key(|(_, pocet)| *pocet) .map(|(vydavatelstvo, _)| vydavatelstvo) } ``` ### Úlohy 10 **10a.** Máš: ```rust struct Zamestnanec { meno: String, oddelenie: String, plat: u32 } ``` Napíš funkciu `pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap`. **10b.** Napíš funkciu `celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap` — 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` — oddelenie kde je najväčší celkový plat. --- --- --- # RIEŠENIA ## 1a ```rust let mut ovocie: HashMap = 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 ```rust let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")]; let mapa: HashMap<&str, &str> = data.into_iter().collect(); ``` ## 2a ```rust if let Some(cena) = ovocie.get("Jablko") { println!("Cena: {} €", cena); } else { println!("Nemáme"); } ``` ## 2b ```rust fn je_v_mape(mapa: &HashMap, kluc: &str) -> bool { mapa.contains_key(kluc) } ``` ## 2c ``` Some(1) None 1 true false ``` ## 3a ```rust 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 ```rust fn odstran_ak_existuje(mapa: &mut HashMap, kluc: &str) -> Option { mapa.remove(kluc) } ``` ## 4a ```rust 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 ```rust fn kluce(mapa: &HashMap) -> Vec<&String> { mapa.keys().collect() } ``` ## 4c ```rust fn zvys_vsetky(mapa: &mut HashMap) { for hodnota in mapa.values_mut() { *hodnota += 10; } } ``` ## 4d ```rust fn sucet_hodnot(mapa: &HashMap) -> i32 { mapa.values().sum() } ``` ## 5a ```rust fn pridaj_body(mapa: &mut HashMap, meno: &str, body: u32) -> bool { if let Some(aktualne) = mapa.get_mut(meno) { *aktualne += body; true } else { false } } ``` ## 5b ```rust fn zdvojnasob(mapa: &mut HashMap, kluc: &str) -> Option { let hodnota = mapa.get_mut(kluc)?; *hodnota *= 2; Some(*hodnota) } ``` ## 6a ```rust let text = "abrakadabra"; let mut pocty: HashMap = HashMap::new(); for c in text.chars() { *pocty.entry(c).or_insert(0) += 1; } for (znak, pocet) in &pocty { println!("{}: {}", znak, pocet); } ``` ## 6b ```rust let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5]; let mut pocty: HashMap = 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 ```rust let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"]; let mut skupiny: HashMap> = HashMap::new(); for slovo in &slova { if let Some(prvy) = slovo.chars().next() { skupiny.entry(prvy).or_default().push(slovo.to_string()); } } ``` ## 7b ```rust let data = vec![ ("Anna", "Matematika"), ("Boris", "Fyzika"), ("Anna", "Fyzika"), ("Boris", "Chémia"), ("Anna", "Chémia"), ]; let mut mapa: HashMap> = HashMap::new(); for (student, predmet) in &data { mapa.entry(student.to_string()).or_default().push(predmet.to_string()); } ``` ## 8a ```rust let mena = vec!["Anna", "Boris", "Anna", "Cyril", "Boris", "Anna"]; let mut pocty: HashMap = 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 ```rust let mut produkty: HashMap = HashMap::new(); produkty.entry("mlieko".to_string()) .and_modify(|(_, pocet)| *pocet += 1) .or_insert((10.0, 1)); ``` ## 9a ```rust fn najdrahsi(mapa: &HashMap) -> Option<&String> { mapa.iter() .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()) .map(|(nazov, _)| nazov) } ``` ## 9b ```rust fn velke_mesta(mapa: &HashMap) -> Vec<&String> { mapa.iter() .filter(|(_, pop)| **pop > 100_000) .map(|(mesto, _)| mesto) .collect() } ``` ## 9c ```rust fn celkovy_pocet(mapa: &HashMap>) -> usize { mapa.values().map(|v| v.len()).sum() } ``` ## 10a ```rust fn pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap { let mut mapa = HashMap::new(); for z in zamestnanci { *mapa.entry(z.oddelenie.clone()).or_insert(0) += 1; } mapa } ``` ## 10b ```rust fn celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap { let mut mapa: HashMap = HashMap::new(); for z in zamestnanci { *mapa.entry(z.oddelenie.clone()).or_insert(0) += z.plat; } mapa } ``` ## 10c ```rust 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 ```rust fn oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option { let platy = celkovy_plat_na_oddeleni(zamestnanci); platy.into_iter() .max_by_key(|(_, plat)| *plat) .map(|(oddelenie, _)| oddelenie) } ```