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

1066 lines
28 KiB
Markdown

# 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<String, i32> = HashMap::new();
// Alebo nechaj Rust odvodiť:
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25); // teraz Rust vie: HashMap<String, i32>
```
### S kapacitou
Ak vieš dopredu koľko prvkov budeš mať, môžeš predalokovať:
```rust
let mut mapa: HashMap<String, i32> = 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<V> (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<String, i32> = 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<String, i32> = /* ... */;
// 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<V>
let odstraneny: Option<i32> = mapa.remove("Anna");
// Some(25) — Anna bola odstránená
let odstraneny: Option<i32> = 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<String, i32> = 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<String, usize> = 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<String, usize> = 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<String, Vec<String>> = 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<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);
```
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<String, usize> {
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<String, Vec<&'a Kniha>> {
let mut mapa: HashMap<String, Vec<&Kniha>> = 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<String> {
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<String, usize>` 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<String, usize>`
- b) `celkova_suma_podla_zakaznika(objednavky: &[Objednavka]) -> HashMap<String, f64>`
- c) `objednavky_podla_mesta(objednavky: &[Objednavka]) -> HashMap<String, Vec<&Objednavka>>`
- d) `mesto_s_najviac_objednavkami(objednavky: &[Objednavka]) -> Option<String>`
- e) `najlepsi_zakaznik(objednavky: &[Objednavka]) -> Option<String>` — zákazník s najvyššou celkovou sumou
**Úloha HM-3:** Napíš funkciu `invertuj(mapa: &HashMap<String, Vec<String>>) -> HashMap<String, Vec<String>>`. 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<char>` — 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<String> = HashSet::new();
// Alebo nechaj odvodiť:
let mut mnozina = HashSet::new();
mnozina.insert("Anna".to_string()); // teraz Rust vie: HashSet<String>
```
### Z iterátora (collect)
```rust
let cisla = vec![1, 2, 3, 2, 1, 4, 3, 5];
let unikatne: HashSet<i32> = cisla.into_iter().collect();
// {1, 2, 3, 4, 5} — duplikáty zmizli
// Z reťazca — množina znakov
let znaky: HashSet<char> = "abrakadabra".chars().collect();
// {'a', 'b', 'r', 'k', 'd'}
```
### S kapacitou
```rust
let mut mnozina: HashSet<i32> = 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<String>`.
---
## 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<i32> = 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<i32> = vec![1, 2, 3, 4].into_iter().collect();
let b: HashSet<i32> = 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<i32> = vec![1, 2].into_iter().collect();
let velka: HashSet<i32> = 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<String> {
let mnozina: HashSet<String> = 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<char>,
skusane_pismena: HashSet<char>,
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<V>` | `.insert(hodnota)``bool` |
| Vyhľadanie | `.get(&kľúč)``Option<&V>` | `.contains(&hodnota)``bool` |
| Mazanie | `.remove(&kľúč)``Option<V>` | `.remove(&hodnota)``bool` |
| Entry API | áno | nie (nepotrebné) |
| Množinové operácie | nie | áno (intersection, union, ...) |
HashSet je vnútorne implementovaný ako `HashMap<T, ()>` — teda HashMap kde hodnota je prázdna.
---
## 21. HashSet — Úloha na precvičenie
**Úloha HS-1:** Napíš funkciu `unikatne_znaky(text: &str) -> HashSet<char>` — 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<char>` — 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<String>` — mená v oboch
- b) `len_v_prvom(a: &[String], b: &[String]) -> Vec<String>` — mená len v prvom
**Úloha HS-6:** Implementuj štruktúru `Slovnik` pre obesenca:
```rust
struct Slovnik {
slova: HashMap<String, Vec<String>>, // 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<usize>` — None ak kategória neexistuje
---
## 22. Kombinovaná úloha — HashMap + HashSet spolu
**Úloha K-1:** Máš:
```rust
struct Student {
meno: String,
predmety: HashSet<String>,
znamky: HashMap<String, u8>, // 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<f64>` — priemer známok, None ak žiadne
- d) `fn spolocne_predmety(s1: &Student, s2: &Student) -> HashSet<String>` — 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<Student>,
}
```
Napíš:
- a) `fn vsetky_predmety(&self) -> HashSet<String>` — 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<f64>` — 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<String, usize> {
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<String, usize> {
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<String, f64> {
let mut mapa: HashMap<String, f64> = 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<String, Vec<&'a Objednavka>> {
let mut mapa: HashMap<String, Vec<&Objednavka>> = 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<String> {
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<String> {
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<String, Vec<String>>) -> HashMap<String, Vec<String>> {
let mut nova: HashMap<String, Vec<String>> = 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<char> {
let mut pocty: HashMap<char, usize> = 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<char> {
text.chars().filter(|c| *c != ' ').collect()
}
```
## HS-2
```rust
fn spolocne_znaky(text1: &str, text2: &str) -> HashSet<char> {
let a: HashSet<char> = text1.chars().collect();
let b: HashSet<char> = 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<String> = text.split_whitespace()
.map(|s| s.to_lowercase())
.collect();
mnozina.len()
}
```
## HS-5
```rust
// a)
fn spolocni(a: &[String], b: &[String]) -> Vec<String> {
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<String> {
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<String, Vec<String>>,
}
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<usize> {
self.slova.get(kategoria).map(|v| v.len())
}
}
```
## K-1
```rust
struct Student {
meno: String,
predmety: HashSet<String>,
znamky: HashMap<String, u8>,
}
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<f64> {
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<String> {
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<Student>,
}
impl Skola {
// a)
fn vsetky_predmety(&self) -> HashSet<String> {
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<String> {
// 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<f64> {
let znamky: Vec<u8> = 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()
})
}
}
```