From d26cd97aa961967b3f90484ba7d21bc128464380 Mon Sep 17 00:00:00 2001 From: Priec Date: Sat, 21 Feb 2026 18:52:22 +0100 Subject: [PATCH] priprava 4 --- JR-priprava-na-skusku6/src/lib.rs | 2 + JR-priprava-na-skusku6/src/main.rs | 6 + JR-priprava-na-skusku6/src/priprava3.rs | 35 + priprava/rust_priprava4.md | 1065 +++++++++++++++++++++++ 4 files changed, 1108 insertions(+) create mode 100644 JR-priprava-na-skusku6/src/priprava3.rs create mode 100644 priprava/rust_priprava4.md diff --git a/JR-priprava-na-skusku6/src/lib.rs b/JR-priprava-na-skusku6/src/lib.rs index 9ce229e..75f2415 100644 --- a/JR-priprava-na-skusku6/src/lib.rs +++ b/JR-priprava-na-skusku6/src/lib.rs @@ -1,3 +1,5 @@ +pub mod priprava3; + use serde::Deserialize; use serde::Serialize; use std::fmt; diff --git a/JR-priprava-na-skusku6/src/main.rs b/JR-priprava-na-skusku6/src/main.rs index 3211c9a..59b444b 100644 --- a/JR-priprava-na-skusku6/src/main.rs +++ b/JR-priprava-na-skusku6/src/main.rs @@ -1,4 +1,7 @@ +use JR_priprava_na_skusku6::priprava3; + fn main() { + priprava3::spusti(); match std::fs::read_to_string("subor.txt") { Ok(x) => print!("{x}"), Err(y) => print!("{y}"), @@ -56,3 +59,6 @@ fn nacitaj_json(cesta: &str) -> Option> { let vec = serde_json::from_str::>(nacitane.as_str()); Some(vec.ok()?) } + + + diff --git a/JR-priprava-na-skusku6/src/priprava3.rs b/JR-priprava-na-skusku6/src/priprava3.rs new file mode 100644 index 0000000..e55cd5e --- /dev/null +++ b/JR-priprava-na-skusku6/src/priprava3.rs @@ -0,0 +1,35 @@ +use std::fmt; +use std::collections::HashMap; + +pub fn spusti() { + let mut mapa: HashMap = HashMap::new(); + // Vloženie + mapa.insert("Anna".to_string(), 25); + mapa.insert("Boris".to_string(), 30); + + // Čítanie — vráti Option<&V> + let vek: Option<&i32> = mapa.get("Anna"); // Some(&25) + let vek: Option<&i32> = mapa.get("Cyril"); // None + + // Kontrola existencie + let existuje: bool = mapa.contains_key("Anna"); // true + + // Iterácia + for (meno, vek) in &mapa { + println!("{}: {}", meno, vek); + } +} + + +struct Kniha { nazov: String, rok: u16 } + +impl fmt::Display for Kniha { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}, {})", self.nazov, self.rok) + } +} + +#[derive(Debug)] +struct Zamestnanec { plat: u32 } + +struct Student { meno: String, vek: u8, priemer: f32 } diff --git a/priprava/rust_priprava4.md b/priprava/rust_priprava4.md new file mode 100644 index 0000000..cfebcc5 --- /dev/null +++ b/priprava/rust_priprava4.md @@ -0,0 +1,1065 @@ +# Rust — HashMap a HashSet kompletný sprievodca + +--- + +## 1. HashMap — čo to je + +HashMap je kolekcia párov **kľúč → hodnota**. Každý kľúč je unikátny. Vyhľadávanie podľa kľúča je veľmi rýchle (O(1)). + +Predstav si to ako slovník: slovo (kľúč) → definícia (hodnota). + +```rust +use std::collections::HashMap; +``` + +Toto musíš importovať vždy. Nie je v prelude (na rozdiel od Vec alebo String). + +--- + +## 2. HashMap — vytváranie + +### Prázdna mapa + +```rust +// Musíš uviesť typy, alebo ich Rust odvodí z prvého insertu +let mut mapa: HashMap = HashMap::new(); + +// Alebo nechaj Rust odvodiť: +let mut mapa = HashMap::new(); +mapa.insert("Anna".to_string(), 25); // teraz Rust vie: HashMap +``` + +### S kapacitou + +Ak vieš dopredu koľko prvkov budeš mať, môžeš predalokovať: + +```rust +let mut mapa: HashMap = HashMap::with_capacity(100); +``` + +### Z iterátora (collect) + +```rust +// Z vektora dvojíc +let dvojice = vec![("Anna", 25), ("Boris", 30), ("Cyril", 22)]; +let mapa: HashMap<&str, i32> = dvojice.into_iter().collect(); + +// Z dvoch vektorov (zip) +let mena = vec!["Anna", "Boris", "Cyril"]; +let veky = vec![25, 30, 22]; +let mapa: HashMap<&str, i32> = mena.into_iter().zip(veky.into_iter()).collect(); +``` + +--- + +## 3. HashMap — vkladanie (insert) + +```rust +let mut mapa = HashMap::new(); + +// Základný insert — vráti Option (starú hodnotu ak kľúč existoval) +let stara = mapa.insert("Anna".to_string(), 25); +// stara = None (kľúč neexistoval) + +let stara = mapa.insert("Anna".to_string(), 26); +// stara = Some(25) (kľúč existoval, stará hodnota bola 25, teraz je 26) +``` + +**Dôležité:** `insert` vždy prepíše hodnotu ak kľúč existuje. Ak chceš vložiť len keď neexistuje, použi `entry` API (sekcia 7). + +--- + +## 4. HashMap — čítanie + +### get — vráti Option<&V> + +```rust +let mut mapa = HashMap::new(); +mapa.insert("Anna".to_string(), 25); + +// get berie referenciu na kľúč +let vek: Option<&i32> = mapa.get("Anna"); // Some(&25) +let vek: Option<&i32> = mapa.get("Boris"); // None + +// Ak chceš hodnotu rovno: +if let Some(vek) = mapa.get("Anna") { + println!("Anna má {} rokov", vek); +} + +// Alebo s unwrap_or: +let vek = mapa.get("Anna").unwrap_or(&0); // &25 +let vek = mapa.get("Boris").unwrap_or(&0); // &0 +``` + +### get s &String vs &str + +```rust +let mut mapa: HashMap = HashMap::new(); +mapa.insert("Anna".to_string(), 25); + +// Obe fungujú — Rust automaticky konvertuje &str na &String pri vyhľadávaní +mapa.get("Anna"); // OK +mapa.get(&"Anna".to_string()); // OK, ale zbytočné +``` + +### Hranatá zátvorka — panické čítanie + +```rust +let vek = mapa["Anna"]; // 25 — ak kľúč neexistuje, PANIC! +// let vek = mapa["Boris"]; // PANIC! program spadne +``` + +**Pravidlo:** Na skúške vždy použi `.get()`, nikdy `[]`. Nechceš panic. + +### get_mut — meniteľná referencia + +```rust +let mut mapa = HashMap::new(); +mapa.insert("Anna".to_string(), 25); + +// Zmeň hodnotu na mieste +if let Some(vek) = mapa.get_mut("Anna") { + *vek += 1; // Anna má teraz 26 +} +``` + +--- + +## 5. HashMap — kontrola existencie + +```rust +let mapa: HashMap = /* ... */; + +// contains_key — vráti bool +let existuje: bool = mapa.contains_key("Anna"); + +// Dĺžka +let pocet: usize = mapa.len(); + +// Prázdna? +let prazdna: bool = mapa.is_empty(); +``` + +--- + +## 6. HashMap — mazanie + +```rust +let mut mapa = HashMap::new(); +mapa.insert("Anna".to_string(), 25); +mapa.insert("Boris".to_string(), 30); + +// remove — vráti Option +let odstraneny: Option = mapa.remove("Anna"); +// Some(25) — Anna bola odstránená + +let odstraneny: Option = mapa.remove("Cyril"); +// None — Cyril neexistoval + +// Vymaž všetko +mapa.clear(); +``` + +--- + +## 7. HashMap — Entry API (NAJDÔLEŽITEJŠIE!) + +Entry API je spôsob, ako efektívne pracovať s kľúčom, ktorý môže alebo nemusí existovať. Toto je na skúške skoro vždy. + +### or_insert — vlož ak neexistuje + +```rust +let mut mapa: HashMap = HashMap::new(); + +// Ak "Anna" neexistuje, vlož 25. Ak existuje, nechaj. +// Vráti &mut na hodnotu (novú alebo existujúcu). +mapa.entry("Anna".to_string()).or_insert(25); +// mapa = {"Anna": 25} + +mapa.entry("Anna".to_string()).or_insert(99); +// mapa = {"Anna": 25} — 99 sa NEVLOŽÍ, lebo Anna už existuje +``` + +### Počítanie výskytov — najčastejší vzor + +```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; +} +// pocty = {"ahoj": 3, "svet": 2, "rust": 1} +``` + +Rozklad po krokoch: +``` +1. pocty.entry("ahoj".to_string()) + → kľúč "ahoj" neexistuje → Vacant entry + +2. .or_insert(0) + → vlož 0 → vráť &mut 0 + +3. *(&mut 0) += 1 + → dereferencuj a zvýš → hodnota je teraz 1 + +Druhý prechod "ahoj": +1. pocty.entry("ahoj".to_string()) + → kľúč "ahoj" existuje → Occupied entry + +2. .or_insert(0) + → kľúč existuje, nevkladaj nič → vráť &mut 1 + +3. *(&mut 1) += 1 + → hodnota je teraz 2 +``` + +### or_insert_with — lazy vloženie + +```rust +// or_insert vždy vyhodnotí argument (aj keď ho nevloží) +mapa.entry(kluc).or_insert(drahy_vypocet()); // drahy_vypocet() sa zavolá VŽDY + +// or_insert_with zavolá closure LEN ak kľúč neexistuje +mapa.entry(kluc).or_insert_with(|| drahy_vypocet()); // zavolá sa len ak treba +``` + +### or_default — použi Default trait + +```rust +// Pre usize → 0, pre String → "", pre Vec → vec![], pre bool → false +let mut pocty: HashMap = HashMap::new(); +*pocty.entry("ahoj".to_string()).or_default() += 1; +// or_default() je to isté ako or_insert(0) pre usize + +// Zoskupovanie do vektorov: +let mut skupiny: HashMap> = HashMap::new(); +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()); +// skupiny = {"ovocie": ["jablko", "hruška"], "zelenina": ["mrkva"]} +``` + +### and_modify — uprav existujúcu hodnotu + +```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); +``` + +Toto je alternatíva k vzoru `*entry.or_insert(0) += 1`, ale je explicitnejšie keď modifikácia a vloženie robia rôzne veci. + +--- + +## 8. HashMap — 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); + +// Iterácia cez páry (kľúč, hodnota) — 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á iterácia cez hodnoty +for vek in mapa.values_mut() { + *vek += 1; // všetkým pridaj rok +} + +// Iterátor + collect — napr. kľúče do Vec +let mena: Vec<&String> = mapa.keys().collect(); + +// Iterátor + filter +let stari: Vec<(&String, &i32)> = mapa.iter().filter(|(_, vek)| **vek > 25).collect(); +``` + +**Dôležité:** HashMap negarantuje poradie! Ak chceš zoradené, musíš po iterácii triediť alebo použiť BTreeMap. + +--- + +## 9. HashMap — praktické vzory zo skúšky + +### Vzor 1: Počítanie podľa kategórie + +```rust +struct Kniha { nazov: String, zaner: String } + +fn pocty_zanrov(knihy: &[Kniha]) -> HashMap { + let mut mapa = HashMap::new(); + for kniha in knihy { + *mapa.entry(kniha.zaner.clone()).or_insert(0) += 1; + } + mapa +} +``` + +### Vzor 2: Výpis štatistík + +```rust +fn vypis_vydavatelstva_a_pocet(knihy: &[Kniha]) { + let pocty = pocty_zanrov(knihy); + for (zaner, pocet) in &pocty { + println!("{}: {}", zaner, pocet); + } +} +``` + +### Vzor 3: Zoskupenie do vektorov + +```rust +fn knihy_podla_zanru<'a>(knihy: &'a [Kniha]) -> HashMap> { + let mut mapa: HashMap> = HashMap::new(); + for kniha in knihy { + mapa.entry(kniha.zaner.clone()).or_default().push(kniha); + } + mapa +} +``` + +### Vzor 4: Najdi najčastejší + +```rust +fn najcastejsi_zaner(knihy: &[Kniha]) -> Option { + let pocty = pocty_zanrov(knihy); + pocty.into_iter() + .max_by_key(|(_, pocet)| *pocet) + .map(|(zaner, _)| zaner) +} +``` + +--- + +## 10. HashMap — Úloha na precvičenie + +**Úloha HM-1:** Napíš funkciu `pocitaj_slova(text: &str) -> HashMap` ktorá rozdelí text na slová (`.split_whitespace()`) a spočíta výskyt každého slova. Preveď slová na lowercase (`.to_lowercase()`). + +**Úloha HM-2:** Máš: +```rust +struct Objednavka { zakaznik: String, suma: f64, mesto: String } +``` +Napíš: +- a) `pocet_objednavok_podla_mesta(objednavky: &[Objednavka]) -> HashMap` +- b) `celkova_suma_podla_zakaznika(objednavky: &[Objednavka]) -> HashMap` +- c) `objednavky_podla_mesta(objednavky: &[Objednavka]) -> HashMap>` +- d) `mesto_s_najviac_objednavkami(objednavky: &[Objednavka]) -> Option` +- e) `najlepsi_zakaznik(objednavky: &[Objednavka]) -> Option` — zákazník s najvyššou celkovou sumou + +**Úloha HM-3:** Napíš funkciu `invertuj(mapa: &HashMap>) -> HashMap>`. Vstup: kategórie → položky. Výstup: položka → kategórie. Príklad: +``` +Vstup: {"ovocie": ["jablko", "hruška"], "červené": ["jablko", "jahoda"]} +Výstup: {"jablko": ["ovocie", "červené"], "hruška": ["ovocie"], "jahoda": ["červené"]} +``` + +**Úloha HM-4:** Napíš funkciu `najcastejsi_znak(text: &str) -> Option` — vráti najčastejší znak v texte (ignoruj medzery). Ak je text prázdny, vráť None. + +--- + +--- + +## 11. HashSet — čo to je + +HashSet je kolekcia **unikátnych hodnôt**. Žiadna hodnota sa nemôže opakovať. Je to vlastne HashMap kde ťa zaujímajú len kľúče, nie hodnoty. + +Predstav si to ako množinu v matematike: {1, 2, 3}. + +```rust +use std::collections::HashSet; +``` + +--- + +## 12. HashSet — vytváranie + +### Prázdna množina + +```rust +let mut mnozina: HashSet = HashSet::new(); + +// Alebo nechaj odvodiť: +let mut mnozina = HashSet::new(); +mnozina.insert("Anna".to_string()); // teraz Rust vie: HashSet +``` + +### Z iterátora (collect) + +```rust +let cisla = vec![1, 2, 3, 2, 1, 4, 3, 5]; +let unikatne: HashSet = cisla.into_iter().collect(); +// {1, 2, 3, 4, 5} — duplikáty zmizli + +// Z reťazca — množina znakov +let znaky: HashSet = "abrakadabra".chars().collect(); +// {'a', 'b', 'r', 'k', 'd'} +``` + +### S kapacitou + +```rust +let mut mnozina: HashSet = HashSet::with_capacity(100); +``` + +--- + +## 13. HashSet — vkladanie (insert) + +```rust +let mut mnozina = HashSet::new(); + +// insert vráti bool — true ak sa vložilo (prvok neexistoval), false ak už existoval +let nova: bool = mnozina.insert("Anna".to_string()); +// true — Anna bola pridaná + +let nova: bool = mnozina.insert("Anna".to_string()); +// false — Anna už existuje, nič sa nezmenilo +``` + +Toto je užitočné na kontrolu duplikátov: +```rust +if !mnozina.insert(hodnota) { + println!("Duplikát!"); +} +``` + +--- + +## 14. HashSet — kontrola existencie (contains) + +```rust +let mut mnozina = HashSet::new(); +mnozina.insert("Anna".to_string()); +mnozina.insert("Boris".to_string()); + +let existuje: bool = mnozina.contains("Anna"); // true +let existuje: bool = mnozina.contains("Cyril"); // false + +// Dĺžka +let pocet: usize = mnozina.len(); // 2 + +// Prázdna? +let prazdna: bool = mnozina.is_empty(); // false +``` + +**contains s &str vs &String:** Rovnako ako HashMap — `contains("Anna")` funguje aj keď máš `HashSet`. + +--- + +## 15. HashSet — mazanie + +```rust +let mut mnozina = HashSet::new(); +mnozina.insert(1); +mnozina.insert(2); +mnozina.insert(3); + +// remove — vráti bool (true ak existoval) +let bol_tam: bool = mnozina.remove(&2); // true, mnozina = {1, 3} +let bol_tam: bool = mnozina.remove(&5); // false, nič sa nezmenilo + +// Vymaž všetko +mnozina.clear(); +``` + +--- + +## 16. HashSet — iterácia + +```rust +let mnozina: HashSet = vec![1, 2, 3, 4, 5].into_iter().collect(); + +// Iterácia — poradie NIE JE garantované! +for prvok in &mnozina { + println!("{}", prvok); +} + +// Iterátor + filter + collect +let parne: HashSet<&i32> = mnozina.iter().filter(|c| *c % 2 == 0).collect(); + +// Do Vec +let vektor: Vec<&i32> = mnozina.iter().collect(); + +// Počet prvkov spĺňajúcich podmienku +let pocet_parnych: usize = mnozina.iter().filter(|c| *c % 2 == 0).count(); +``` + +--- + +## 17. HashSet — množinové operácie + +Toto je to, čo robí HashSet špeciálnym oproti Vec. Tieto operácie sú veľmi efektívne. + +### Prienik (intersection) — prvky v oboch + +```rust +let a: HashSet = vec![1, 2, 3, 4].into_iter().collect(); +let b: HashSet = vec![3, 4, 5, 6].into_iter().collect(); + +// intersection vráti iterátor +let prienik: HashSet<&i32> = a.intersection(&b).collect(); +// {&3, &4} + +// Alebo do Vec +let prienik: Vec<&i32> = a.intersection(&b).collect(); +``` + +### Zjednotenie (union) — prvky v aspoň jednom + +```rust +let zjednotenie: HashSet<&i32> = a.union(&b).collect(); +// {&1, &2, &3, &4, &5, &6} +``` + +### Rozdiel (difference) — prvky v A, ktoré nie sú v B + +```rust +let rozdiel: HashSet<&i32> = a.difference(&b).collect(); +// {&1, &2} + +// Opačný smer: +let rozdiel_opacny: HashSet<&i32> = b.difference(&a).collect(); +// {&5, &6} +``` + +### Symetrický rozdiel — prvky v jednom, ale nie v oboch + +```rust +let sym_rozdiel: HashSet<&i32> = a.symmetric_difference(&b).collect(); +// {&1, &2, &5, &6} +``` + +### Podmnožina a nadmnožina + +```rust +let mala: HashSet = vec![1, 2].into_iter().collect(); +let velka: HashSet = vec![1, 2, 3, 4].into_iter().collect(); + +let je_podmnozina: bool = mala.is_subset(&velka); // true +let je_nadmnozina: bool = velka.is_superset(&mala); // true +let je_disjunktna: bool = mala.is_disjoint(&velka); // false (majú spoločné prvky) +``` + +### Operátory + +```rust +// Tieto operátory fungujú tiež: +let prienik = &a & &b; // intersection +let zjednotenie = &a | &b; // union +let rozdiel = &a - &b; // difference +let sym_rozdiel = &a ^ &b; // symmetric_difference +// Vracajú nový HashSet (nie referencie) +``` + +--- + +## 18. HashSet — praktické použitie v Ruste + +### Vzor 1: Sledovanie už videných hodnôt + +```rust +fn ma_duplikaty(cisla: &[i32]) -> bool { + let mut videne = HashSet::new(); + for c in cisla { + if !videne.insert(c) { + return true; // insert vrátil false → duplikát + } + } + false +} +``` + +### Vzor 2: Unikátne hodnoty z kolekcie + +```rust +fn unikatne_zanre(knihy: &[Kniha]) -> Vec { + let mnozina: HashSet = knihy.iter().map(|k| k.zaner.clone()).collect(); + mnozina.into_iter().collect() +} +``` + +### Vzor 3: Rýchle vyhľadávanie (namiesto Vec.contains) + +```rust +// Vec.contains je pomalé (O(n)) — prechádza celý vektor +let povolene_vec = vec!["admin", "editor", "viewer"]; +povolene_vec.contains(&"admin"); // O(n) + +// HashSet.contains je rýchle (O(1)) +let povolene_set: HashSet<&str> = vec!["admin", "editor", "viewer"].into_iter().collect(); +povolene_set.contains("admin"); // O(1) +``` + +Ak kontroluješ existenciu opakovane (napr. v cykle), vždy použi HashSet. + +### Vzor 4: Obesenec — uhádnuté písmená (zo skúšky!) + +```rust +struct Hra { + hladane_slovo: String, + uhadnute_pismena: HashSet, + skusane_pismena: HashSet, + pocet_zivotov: u8, +} + +impl Hra { + fn new(slovo: &str) -> Hra { + Hra { + hladane_slovo: slovo.to_lowercase(), + uhadnute_pismena: HashSet::new(), + skusane_pismena: HashSet::new(), + pocet_zivotov: 6, + } + } + + fn tipni(&mut self, pismeno: char) { + let p = pismeno.to_lowercase().next().unwrap(); + + // Ak sme už skúšali toto písmeno — preskočíme + if !self.skusane_pismena.insert(p) { + println!("Toto písmeno si už skúšal!"); + return; + } + + // Je písmeno v slove? + if self.hladane_slovo.contains(p) { + self.uhadnute_pismena.insert(p); + println!("Správne!"); + } else { + self.pocet_zivotov -= 1; + println!("Zle! Zostáva životov: {}", self.pocet_zivotov); + } + } + + fn zobraz_slovo(&self) -> String { + self.hladane_slovo.chars().map(|c| { + if self.uhadnute_pismena.contains(&c) { c } else { '_' } + }).collect() + } + + fn je_vyhra(&self) -> bool { + self.hladane_slovo.chars().all(|c| self.uhadnute_pismena.contains(&c)) + } +} +``` + +--- + +## 19. HashSet — kedy HashSet, kedy Vec + +| Situácia | Použi | +|----------|-------| +| Potrebuješ poradie prvkov | Vec | +| Potrebuješ duplikáty | Vec | +| Potrebuješ index (prvý, druhý, ...) | Vec | +| Často kontroluješ `contains` | HashSet | +| Potrebuješ unikátne hodnoty | HashSet | +| Robíš množinové operácie (prienik, ...) | HashSet | +| Sleduješ "už som videl" | HashSet | +| Chceš rýchle pridávanie + kontrolu | HashSet | + +--- + +## 20. HashMap vs HashSet + +| | HashMap | HashSet | +|-|---------|---------| +| Ukladá | kľúč → hodnota | len hodnota | +| Import | `use std::collections::HashMap` | `use std::collections::HashSet` | +| Vloženie | `.insert(kľúč, hodnota)` → `Option` | `.insert(hodnota)` → `bool` | +| Vyhľadanie | `.get(&kľúč)` → `Option<&V>` | `.contains(&hodnota)` → `bool` | +| Mazanie | `.remove(&kľúč)` → `Option` | `.remove(&hodnota)` → `bool` | +| Entry API | áno | nie (nepotrebné) | +| Množinové operácie | nie | áno (intersection, union, ...) | + +HashSet je vnútorne implementovaný ako `HashMap` — teda HashMap kde hodnota je prázdna. + +--- + +## 21. HashSet — Úloha na precvičenie + +**Úloha HS-1:** Napíš funkciu `unikatne_znaky(text: &str) -> HashSet` — vráti množinu všetkých unikátnych znakov v texte (bez medzier). + +**Úloha HS-2:** Napíš funkciu `spolocne_znaky(text1: &str, text2: &str) -> HashSet` — vráti znaky, ktoré sa vyskytujú v oboch textoch. + +**Úloha HS-3:** Napíš funkciu `ma_duplikaty(cisla: &[i32]) -> bool` — vráti true ak vektor obsahuje duplikáty. Použi HashSet. + +**Úloha HS-4:** Napíš funkciu `unikatne_slova(text: &str) -> usize` — vráti počet unikátnych slov v texte (case-insensitive, použi `.to_lowercase()`). + +**Úloha HS-5:** Máš dva vektory mien. Napíš: +- a) `spolocni(a: &[String], b: &[String]) -> Vec` — mená v oboch +- b) `len_v_prvom(a: &[String], b: &[String]) -> Vec` — mená len v prvom + +**Úloha HS-6:** Implementuj štruktúru `Slovnik` pre obesenca: +```rust +struct Slovnik { + slova: HashMap>, // kategória → slová +} +``` +Napíš: +- a) `new() -> Slovnik` +- b) `pridaj_kategoriu(&mut self, kategoria: &str)` — pridá prázdnu kategóriu ak neexistuje +- c) `pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()>` — Err ak kategória neexistuje +- d) `nahodne_slovo(&self, kategoria: &str) -> Option<&String>` — náhodné slovo z kategórie (použi `rand`) +- e) `vsetky_kategorie(&self) -> Vec<&String>` — zoznam kategórií +- f) `pocet_slov_v_kategorii(&self, kategoria: &str) -> Option` — None ak kategória neexistuje + +--- + +## 22. Kombinovaná úloha — HashMap + HashSet spolu + +**Úloha K-1:** Máš: +```rust +struct Student { + meno: String, + predmety: HashSet, + znamky: HashMap, // predmet → známka +} +``` +Napíš: +- a) `fn pridaj_predmet(&mut self, predmet: &str)` — pridá predmet do množiny +- b) `fn zadaj_znamku(&mut self, predmet: &str, znamka: u8) -> Result<(), ()>` — Err ak študent nemá predmet +- c) `fn priemer(&self) -> Option` — priemer známok, None ak žiadne +- d) `fn spolocne_predmety(s1: &Student, s2: &Student) -> HashSet` — predmety oboch +- e) `fn ma_vsetky_znamky(&self) -> bool` — má známku z každého predmetu? + +**Úloha K-2:** Máš: +```rust +struct Skola { + studenti: Vec, +} +``` +Napíš: +- a) `fn vsetky_predmety(&self) -> HashSet` — všetky predmety naprieč všetkými študentmi +- b) `fn studenti_s_predmetom(&self, predmet: &str) -> Vec<&Student>` — študenti zapísaní na predmet +- c) `fn priemer_za_predmet(&self, predmet: &str) -> Option` — priemer známok za predmet naprieč študentmi +- d) `fn najlepsi_student(&self) -> Option<&Student>` — študent s najnižším priemerom (kto má znamky) + +--- + +--- + +--- + +# RIEŠENIA + +## HM-1 + +```rust +fn pocitaj_slova(text: &str) -> HashMap { + let mut mapa = HashMap::new(); + for slovo in text.split_whitespace() { + *mapa.entry(slovo.to_lowercase()).or_insert(0) += 1; + } + mapa +} +``` + +## HM-2 + +```rust +struct Objednavka { zakaznik: String, suma: f64, mesto: String } + +// a) +fn pocet_objednavok_podla_mesta(objednavky: &[Objednavka]) -> HashMap { + let mut mapa = HashMap::new(); + for obj in objednavky { + *mapa.entry(obj.mesto.clone()).or_insert(0) += 1; + } + mapa +} + +// b) +fn celkova_suma_podla_zakaznika(objednavky: &[Objednavka]) -> HashMap { + let mut mapa: HashMap = HashMap::new(); + for obj in objednavky { + *mapa.entry(obj.zakaznik.clone()).or_insert(0.0) += obj.suma; + } + mapa +} + +// c) +fn objednavky_podla_mesta<'a>(objednavky: &'a [Objednavka]) -> HashMap> { + let mut mapa: HashMap> = HashMap::new(); + for obj in objednavky { + mapa.entry(obj.mesto.clone()).or_default().push(obj); + } + mapa +} + +// d) +fn mesto_s_najviac_objednavkami(objednavky: &[Objednavka]) -> Option { + let pocty = pocet_objednavok_podla_mesta(objednavky); + pocty.into_iter() + .max_by_key(|(_, pocet)| *pocet) + .map(|(mesto, _)| mesto) +} + +// e) +fn najlepsi_zakaznik(objednavky: &[Objednavka]) -> Option { + let sumy = celkova_suma_podla_zakaznika(objednavky); + sumy.into_iter() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()) + .map(|(zakaznik, _)| zakaznik) +} +``` + +## HM-3 + +```rust +fn invertuj(mapa: &HashMap>) -> HashMap> { + let mut nova: HashMap> = HashMap::new(); + for (kategoria, polozky) in mapa { + for polozka in polozky { + nova.entry(polozka.clone()).or_default().push(kategoria.clone()); + } + } + nova +} +``` + +## HM-4 + +```rust +fn najcastejsi_znak(text: &str) -> Option { + let mut pocty: HashMap = HashMap::new(); + for c in text.chars() { + if c != ' ' { + *pocty.entry(c).or_insert(0) += 1; + } + } + pocty.into_iter() + .max_by_key(|(_, pocet)| *pocet) + .map(|(znak, _)| znak) +} +``` + +## HS-1 + +```rust +fn unikatne_znaky(text: &str) -> HashSet { + text.chars().filter(|c| *c != ' ').collect() +} +``` + +## HS-2 + +```rust +fn spolocne_znaky(text1: &str, text2: &str) -> HashSet { + let a: HashSet = text1.chars().collect(); + let b: HashSet = text2.chars().collect(); + a.intersection(&b).copied().collect() +} +``` + +## HS-3 + +```rust +fn ma_duplikaty(cisla: &[i32]) -> bool { + let mut videne = HashSet::new(); + for c in cisla { + if !videne.insert(c) { + return true; + } + } + false +} + +// Alebo kratšie (ale menej efektívne — prejde celý vektor): +fn ma_duplikaty(cisla: &[i32]) -> bool { + let mnozina: HashSet<&i32> = cisla.iter().collect(); + mnozina.len() != cisla.len() +} +``` + +## HS-4 + +```rust +fn unikatne_slova(text: &str) -> usize { + let mnozina: HashSet = text.split_whitespace() + .map(|s| s.to_lowercase()) + .collect(); + mnozina.len() +} +``` + +## HS-5 + +```rust +// a) +fn spolocni(a: &[String], b: &[String]) -> Vec { + let set_a: HashSet<&String> = a.iter().collect(); + let set_b: HashSet<&String> = b.iter().collect(); + set_a.intersection(&set_b).map(|s| (*s).clone()).collect() +} + +// b) +fn len_v_prvom(a: &[String], b: &[String]) -> Vec { + let set_a: HashSet<&String> = a.iter().collect(); + let set_b: HashSet<&String> = b.iter().collect(); + set_a.difference(&set_b).map(|s| (*s).clone()).collect() +} +``` + +## HS-6 + +```rust +use rand::seq::SliceRandom; + +struct Slovnik { + slova: HashMap>, +} + +impl Slovnik { + // a) + fn new() -> Slovnik { + Slovnik { slova: HashMap::new() } + } + + // b) + fn pridaj_kategoriu(&mut self, kategoria: &str) { + self.slova.entry(kategoria.to_string()).or_default(); + } + + // c) + fn pridaj_slovo(&mut self, kategoria: &str, slovo: &str) -> Result<(), ()> { + let kat = self.slova.get_mut(kategoria).ok_or(())?; + kat.push(slovo.to_string()); + Ok(()) + } + + // d) + fn nahodne_slovo(&self, kategoria: &str) -> Option<&String> { + let kat = self.slova.get(kategoria)?; + let mut rng = rand::thread_rng(); + kat.choose(&mut rng) + } + + // e) + fn vsetky_kategorie(&self) -> Vec<&String> { + self.slova.keys().collect() + } + + // f) + fn pocet_slov_v_kategorii(&self, kategoria: &str) -> Option { + self.slova.get(kategoria).map(|v| v.len()) + } +} +``` + +## K-1 + +```rust +struct Student { + meno: String, + predmety: HashSet, + znamky: HashMap, +} + +impl Student { + // a) + fn pridaj_predmet(&mut self, predmet: &str) { + self.predmety.insert(predmet.to_string()); + } + + // b) + fn zadaj_znamku(&mut self, predmet: &str, znamka: u8) -> Result<(), ()> { + if !self.predmety.contains(predmet) { + return Err(()); + } + self.znamky.insert(predmet.to_string(), znamka); + Ok(()) + } + + // c) + fn priemer(&self) -> Option { + if self.znamky.is_empty() { + return None; + } + let sucet: u32 = self.znamky.values().map(|z| *z as u32).sum(); + Some(sucet as f64 / self.znamky.len() as f64) + } + + // d) + fn spolocne_predmety(s1: &Student, s2: &Student) -> HashSet { + s1.predmety.intersection(&s2.predmety).cloned().collect() + } + + // e) + fn ma_vsetky_znamky(&self) -> bool { + self.predmety.iter().all(|p| self.znamky.contains_key(p)) + } +} +``` + +## K-2 + +```rust +struct Skola { + studenti: Vec, +} + +impl Skola { + // a) + fn vsetky_predmety(&self) -> HashSet { + let mut vsetky = HashSet::new(); + for student in &self.studenti { + for predmet in &student.predmety { + vsetky.insert(predmet.clone()); + } + } + vsetky + } + + // Alternatíva s iterátormi: + // fn vsetky_predmety(&self) -> HashSet { + // self.studenti.iter() + // .flat_map(|s| s.predmety.iter().cloned()) + // .collect() + // } + + // b) + fn studenti_s_predmetom(&self, predmet: &str) -> Vec<&Student> { + self.studenti.iter() + .filter(|s| s.predmety.contains(predmet)) + .collect() + } + + // c) + fn priemer_za_predmet(&self, predmet: &str) -> Option { + let znamky: Vec = self.studenti.iter() + .filter_map(|s| s.znamky.get(predmet).copied()) + .collect(); + if znamky.is_empty() { + return None; + } + let sucet: u32 = znamky.iter().map(|z| *z as u32).sum(); + Some(sucet as f64 / znamky.len() as f64) + } + + // d) + fn najlepsi_student(&self) -> Option<&Student> { + self.studenti.iter() + .filter(|s| !s.znamky.is_empty()) + .min_by(|a, b| { + let priemer_a = a.priemer().unwrap(); + let priemer_b = b.priemer().unwrap(); + priemer_a.partial_cmp(&priemer_b).unwrap() + }) + } +} +```