28 KiB
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).
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
// 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ť:
let mut mapa: HashMap<String, i32> = HashMap::with_capacity(100);
Z iterátora (collect)
// 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)
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>
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
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
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
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
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
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
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
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
// 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
// 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
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
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
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
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
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ší
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áš:
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}.
use std::collections::HashSet;
12. HashSet — vytváranie
Prázdna množina
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)
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
let mut mnozina: HashSet<i32> = HashSet::with_capacity(100);
13. HashSet — vkladanie (insert)
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:
if !mnozina.insert(hodnota) {
println!("Duplikát!");
}
14. HashSet — kontrola existencie (contains)
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
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
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
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
let zjednotenie: HashSet<&i32> = a.union(&b).collect();
// {&1, &2, &3, &4, &5, &6}
Rozdiel (difference) — prvky v A, ktoré nie sú v B
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
let sym_rozdiel: HashSet<&i32> = a.symmetric_difference(&b).collect();
// {&1, &2, &5, &6}
Podmnožina a nadmnožina
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
// 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
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
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)
// 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!)
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:
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žirand) - 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áš:
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áš:
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
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
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
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
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
fn unikatne_znaky(text: &str) -> HashSet<char> {
text.chars().filter(|c| *c != ' ').collect()
}
HS-2
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
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
fn unikatne_slova(text: &str) -> usize {
let mnozina: HashSet<String> = text.split_whitespace()
.map(|s| s.to_lowercase())
.collect();
mnozina.len()
}
HS-5
// 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
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
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
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()
})
}
}