1066 lines
28 KiB
Markdown
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()
|
|
})
|
|
}
|
|
}
|
|
```
|