18 KiB
HashMap — krok za krokom
Každá kapitola: prečítaj, pozri príklady, urob úlohy. Až potom choď ďalej. Riešenia sú na konci, číslované.
use std::collections::HashMap; // toto treba vždy
Kapitola 1: Vytvorenie a insert
HashMap je kolekcia párov kľúč → hodnota. Kľúč je unikátny.
let mut mapa: HashMap<String, i32> = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// mapa = {"Anna": 25, "Boris": 30}
// insert na existujúci kľúč PREPÍŠE hodnotu a vráti starú
let stara = mapa.insert("Anna".to_string(), 26);
// stara = Some(25), mapa = {"Anna": 26, "Boris": 30}
// insert na nový kľúč vráti None
let stara = mapa.insert("Cyril".to_string(), 22);
// stara = None
Úlohy 1
1a. Vytvor HashMap kde kľúče sú mená ovocia (String) a hodnoty sú ceny (f64). Vlož 3 ovocia.
1b. Čo vráti posledný insert? Napíš kód a over:
let mut m = HashMap::new();
m.insert("x", 1);
m.insert("y", 2);
let vysledok = m.insert("x", 99);
println!("{:?}", vysledok);
1c. Vytvor HashMap z vektora dvojíc pomocou .collect():
let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
// tvoj kód → HashMap<&str, &str>
Kapitola 2: Čítanie — get, contains_key, len
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// get — vráti Option<&V>
let vek = mapa.get("Anna"); // Some(&25)
let vek = mapa.get("Cyril"); // None
// contains_key — vráti bool
let je_tam = mapa.contains_key("Anna"); // true
let je_tam = mapa.contains_key("Cyril"); // false
// len — počet párov
let pocet = mapa.len(); // 2
// is_empty
let prazdna = mapa.is_empty(); // false
Dôležité: Nikdy nepoužívaj mapa["Anna"] — ak kľúč neexistuje, program spadne. Vždy .get().
Úlohy 2
2a. Máš mapu ovocia z úlohy 1a. Napíš kód, ktorý:
- Vypíše cenu jablka ak existuje
- Vypíše "Nemáme" ak neexistuje
Použi
if lets.get().
2b. Napíš funkciu:
fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool
Použi contains_key.
2c. Čo vypíše? Tipni, potom over:
let mut m: HashMap<&str, i32> = HashMap::new();
m.insert("a", 1);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));
println!("{}", m.len());
println!("{}", m.contains_key("a"));
println!("{}", m.contains_key("b"));
Kapitola 3: Mazanie — remove, clear
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
// remove — vráti Option<V> (hodnotu, nie referenciu)
let odstraneny = mapa.remove("Anna");
// odstraneny = Some(25), mapa = {"Boris": 30}
let odstraneny = mapa.remove("Cyril");
// odstraneny = None (neexistoval)
// clear — vymaže všetko
mapa.clear();
// mapa = {}
Úlohy 3
3a. Vytvor mapu s 3 položkami. Odstráň jednu. Vypíš čo remove vrátil. Vypíš veľkosť mapy.
3b. Napíš funkciu:
fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>
Ak kľúč existuje, odstráň ho a vráť hodnotu. Ak nie, vráť None. (Hint: remove presne toto robí.)
Kapitola 4: 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);
// Cez páry — poradie NIE JE garantované!
for (meno, vek) in &mapa {
println!("{}: {}", meno, vek);
}
// Len kľúče
for meno in mapa.keys() {
println!("{}", meno);
}
// Len hodnoty
for vek in mapa.values() {
println!("{}", vek);
}
// Meniteľné hodnoty
for vek in mapa.values_mut() {
*vek += 1;
}
Úlohy 4
4a. Vytvor mapu produktov (názov → cena). Vypíš všetky produkty vo formáte "Produkt: cena €".
4b. Napíš funkciu, ktorá vráti vektor všetkých kľúčov z mapy:
fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String>
4c. Napíš funkciu, ktorá zvýši všetky hodnoty v mape o 10:
fn zvys_vsetky(mapa: &mut HashMap<String, i32>)
4d. Napíš funkciu, ktorá vráti súčet všetkých hodnôt:
fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32
Použi .values() a .sum().
Kapitola 5: get_mut — zmena hodnoty na mieste
Ak chceš zmeniť jednu konkrétnu hodnotu (nie všetky), použi get_mut:
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
// get_mut vráti Option<&mut V>
if let Some(vek) = mapa.get_mut("Anna") {
*vek += 1; // Anna má teraz 26
}
// Ak kľúč neexistuje, get_mut vráti None
if let Some(vek) = mapa.get_mut("Boris") {
*vek += 1; // Toto sa nevykoná
}
Úlohy 5
5a. Máš mapu študentov (meno → počet bodov). Napíš funkciu, ktorá pridá body jednému študentovi:
fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool
Vráti true ak študent existoval, false ak nie.
5b. Napíš funkciu, ktorá zdvojnásobí hodnotu pre daný kľúč:
fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32>
Vráti novú hodnotu ak kľúč existoval, None ak nie.
Kapitola 6: Entry API — or_insert
Toto je najdôležitejšia časť. Entry API rieši situáciu: "ak kľúč neexistuje, vlož default; ak existuje, použi existujúcu hodnotu."
let mut mapa: HashMap<String, i32> = HashMap::new();
// entry() vráti Entry — buď Vacant (prázdny) alebo Occupied (obsadený)
// or_insert(hodnota) — ak Vacant, vlož hodnotu. Vráti &mut na hodnotu.
mapa.entry("Anna".to_string()).or_insert(25);
// Anna neexistovala → vloží 25 → mapa = {"Anna": 25}
mapa.entry("Anna".to_string()).or_insert(99);
// Anna existuje → NEVLOŽÍ 99 → mapa = {"Anna": 25} (nezmenené!)
Hlavné použitie — počítanie výskytov:
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;
}
// {"ahoj": 3, "svet": 2, "rust": 1}
Čo sa deje krok po kroku:
Slovo "ahoj":
entry("ahoj") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
*(&mut 0) += 1 → hodnota je 1
Slovo "svet":
entry("svet") → Vacant → or_insert(0) vloží 0 → vráti &mut 0
*(&mut 0) += 1 → hodnota je 1
Slovo "ahoj" (druhýkrát):
entry("ahoj") → Occupied (hodnota 1) → or_insert(0) NEVLOŽÍ → vráti &mut 1
*(&mut 1) += 1 → hodnota je 2
Úlohy 6
6a. Spočítaj výskyt každého znaku v reťazci "abrakadabra". Použi entry + or_insert. Výsledok vypíš.
6b. Máš vektor čísel [1, 3, 2, 1, 4, 3, 2, 1, 5]. Spočítaj koľkokrát sa vyskytuje každé číslo.
6c. Čo vypíše? Tipni, potom over:
let mut m: HashMap<&str, i32> = HashMap::new();
m.entry("a").or_insert(10);
m.entry("b").or_insert(20);
m.entry("a").or_insert(99);
m.insert("b", 99);
println!("{:?}", m.get("a"));
println!("{:?}", m.get("b"));
Kapitola 7: Entry API — or_default
or_default() je skratka za or_insert(Default::default()). Vloží defaultnú hodnotu pre daný typ.
| Typ | Default |
|---|---|
| usize, i32, u32... | 0 |
| f64 | 0.0 |
| bool | false |
| String | "" |
| Vec | vec![] |
let mut pocty: HashMap<String, usize> = HashMap::new();
// Tieto dva riadky robia to isté:
*pocty.entry("ahoj".to_string()).or_insert(0) += 1;
*pocty.entry("ahoj".to_string()).or_default() += 1;
Hlavné použitie — zoskupovanie do vektorov:
let mut skupiny: HashMap<String, Vec<String>> = HashMap::new();
// or_default() pre Vec vloží prázdny vektor
skupiny.entry("ovocie".to_string()).or_default().push("jablko".to_string());
skupiny.entry("ovocie".to_string()).or_default().push("hruška".to_string());
skupiny.entry("zelenina".to_string()).or_default().push("mrkva".to_string());
// {"ovocie": ["jablko", "hruška"], "zelenina": ["mrkva"]}
Úlohy 7
7a. Máš vektor slov. Zoskup ich podľa prvého písmena do HashMap<char, Vec>:
let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
// Výsledok: {'a': ["auto", "ahoj", "auto"], 'b': ["banán", "breza"], 'c': ["citrón"]}
7b. Máš vektor dvojíc (študent, predmet). Zoskup predmety podľa študenta:
let data = vec![
("Anna", "Matematika"),
("Boris", "Fyzika"),
("Anna", "Fyzika"),
("Boris", "Chémia"),
("Anna", "Chémia"),
];
// Výsledok: {"Anna": ["Matematika", "Fyzika", "Chémia"], "Boris": ["Fyzika", "Chémia"]}
Kapitola 8: Entry API — and_modify
and_modify ti umožní spraviť niečo s existujúcou hodnotou pred tým, ako sa rozhodne čo robiť:
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);
// Anna neexistovala → vloží 1
mapa.entry("Anna".to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
// Anna existuje s hodnotou 1 → and_modify zvýši na 2
Toto je užitočné keď chceš robiť rôzne veci pre nový vs existujúci kľúč.
Úlohy 8
8a. Máš vektor mien. Pre každé meno: ak sa vyskytlo prvýkrát, vlož 1. Ak sa vyskytlo znovu, zvýš o 1. Použi and_modify + or_insert. (Áno, výsledok je rovnaký ako cez or_insert(0) += 1, ale precvič si syntax.)
8b. Máš mapu produktov (String → (cena: f64, počet: u32)). Napíš kód, ktorý: ak produkt existuje, zvýši počet o 1. Ak neexistuje, vloží ho s cenou 10.0 a počtom 1.
let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
// tvoj kód pre produkt "mlieko"
Kapitola 9: Iterátory s HashMap
HashMap sa dá kombinovať s iterátorovými metódami:
let mut mapa = HashMap::new();
mapa.insert("Anna".to_string(), 25);
mapa.insert("Boris".to_string(), 30);
mapa.insert("Cyril".to_string(), 17);
// filter — nechaj len dospelých
let dospeli: HashMap<&String, &i32> = mapa.iter()
.filter(|(_, vek)| **vek >= 18)
.collect();
// find — nájdi prvého s daným menom
let boris: Option<(&String, &i32)> = mapa.iter()
.find(|(meno, _)| meno.as_str() == "Boris");
// max_by_key — najstarší
let najstarsi: Option<(&String, &i32)> = mapa.iter()
.max_by_key(|(_, vek)| *vek);
// Kľúče do Vec
let mena: Vec<&String> = mapa.keys().collect();
// Hodnoty — súčet
let sucet_vekov: i32 = mapa.values().sum();
Úlohy 9
9a. Máš HashMap<String, f64> (produkt → cena). Nájdi najdrahší produkt. Vráť jeho názov ako Option<&String>.
9b. Máš HashMap<String, u32> (mesto → populácia). Vráť Vec miest s populáciou nad 100_000.
9c. Máš HashMap<String, Vec<String>> (kategória → produkty). Nájdi celkový počet produktov naprieč všetkými kategóriami. Použi .values() a .map() a .sum().
Kapitola 10: Praktické vzory zo skúšky
Vzor 1: Počítanie podľa poľa štruktúry
struct Kniha { nazov: String, vydavatelstvo: String }
fn pocty_podla_vydavatelstva(knihy: &[Kniha]) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for kniha in knihy {
*mapa.entry(kniha.vydavatelstvo.clone()).or_insert(0) += 1;
}
mapa
}
Vzor 2: Výpis vo formáte "Kľúč: hodnota"
fn vypis_statistiky(knihy: &[Kniha]) {
let pocty = pocty_podla_vydavatelstva(knihy);
for (vydavatelstvo, pocet) in &pocty {
println!("{}: {}", vydavatelstvo, pocet);
}
}
Vzor 3: Nájdi najčastejší
fn najcastejsie_vydavatelstvo(knihy: &[Kniha]) -> Option<String> {
let pocty = pocty_podla_vydavatelstva(knihy);
pocty.into_iter()
.max_by_key(|(_, pocet)| *pocet)
.map(|(vydavatelstvo, _)| vydavatelstvo)
}
Úlohy 10
10a. Máš:
struct Zamestnanec { meno: String, oddelenie: String, plat: u32 }
Napíš funkciu pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize>.
10b. Napíš funkciu celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> — súčet platov na každom oddelení.
10c. Napíš funkciu vypis_statistiky(zamestnanci: &[Zamestnanec]) — pre každé oddelenie vypíše počet ľudí a celkový plat.
10d. Napíš funkciu oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> — oddelenie kde je najväčší celkový plat.
RIEŠENIA
1a
let mut ovocie: HashMap<String, f64> = HashMap::new();
ovocie.insert("Jablko".to_string(), 1.50);
ovocie.insert("Banán".to_string(), 2.00);
ovocie.insert("Pomaranč".to_string(), 2.50);
1b
Some(1)
Lebo "x" už existoval s hodnotou 1, insert ho prepísal na 99 a vrátil starú hodnotu.
1c
let data = vec![("sk", "Slovensko"), ("cz", "Česko"), ("pl", "Poľsko")];
let mapa: HashMap<&str, &str> = data.into_iter().collect();
2a
if let Some(cena) = ovocie.get("Jablko") {
println!("Cena: {} €", cena);
} else {
println!("Nemáme");
}
2b
fn je_v_mape(mapa: &HashMap<String, i32>, kluc: &str) -> bool {
mapa.contains_key(kluc)
}
2c
Some(1)
None
1
true
false
3a
let mut mapa = HashMap::new();
mapa.insert("A".to_string(), 1);
mapa.insert("B".to_string(), 2);
mapa.insert("C".to_string(), 3);
let odstraneny = mapa.remove("B");
println!("{:?}", odstraneny); // Some(2)
println!("{}", mapa.len()); // 2
3b
fn odstran_ak_existuje(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
mapa.remove(kluc)
}
4a
let mut produkty = HashMap::new();
produkty.insert("Chlieb".to_string(), 1.20);
produkty.insert("Mlieko".to_string(), 0.89);
produkty.insert("Maslo".to_string(), 2.50);
for (nazov, cena) in &produkty {
println!("{}: {} €", nazov, cena);
}
4b
fn kluce(mapa: &HashMap<String, i32>) -> Vec<&String> {
mapa.keys().collect()
}
4c
fn zvys_vsetky(mapa: &mut HashMap<String, i32>) {
for hodnota in mapa.values_mut() {
*hodnota += 10;
}
}
4d
fn sucet_hodnot(mapa: &HashMap<String, i32>) -> i32 {
mapa.values().sum()
}
5a
fn pridaj_body(mapa: &mut HashMap<String, u32>, meno: &str, body: u32) -> bool {
if let Some(aktualne) = mapa.get_mut(meno) {
*aktualne += body;
true
} else {
false
}
}
5b
fn zdvojnasob(mapa: &mut HashMap<String, i32>, kluc: &str) -> Option<i32> {
let hodnota = mapa.get_mut(kluc)?;
*hodnota *= 2;
Some(*hodnota)
}
6a
let text = "abrakadabra";
let mut pocty: HashMap<char, usize> = HashMap::new();
for c in text.chars() {
*pocty.entry(c).or_insert(0) += 1;
}
for (znak, pocet) in &pocty {
println!("{}: {}", znak, pocet);
}
6b
let cisla = vec![1, 3, 2, 1, 4, 3, 2, 1, 5];
let mut pocty: HashMap<i32, usize> = HashMap::new();
for c in &cisla {
*pocty.entry(*c).or_insert(0) += 1;
}
// {1: 3, 3: 2, 2: 2, 4: 1, 5: 1}
6c
Some(10)
Some(99)
entry("a").or_insert(99) — "a" už existuje, takže 99 sa NEVLOŽÍ, ostáva 10.
insert("b", 99) — insert VŽDY prepíše, takže "b" je teraz 99.
7a
let slova = vec!["auto", "ahoj", "banán", "breza", "citrón", "auto"];
let mut skupiny: HashMap<char, Vec<String>> = HashMap::new();
for slovo in &slova {
if let Some(prvy) = slovo.chars().next() {
skupiny.entry(prvy).or_default().push(slovo.to_string());
}
}
7b
let data = vec![
("Anna", "Matematika"),
("Boris", "Fyzika"),
("Anna", "Fyzika"),
("Boris", "Chémia"),
("Anna", "Chémia"),
];
let mut mapa: HashMap<String, Vec<String>> = HashMap::new();
for (student, predmet) in &data {
mapa.entry(student.to_string()).or_default().push(predmet.to_string());
}
8a
let mena = vec!["Anna", "Boris", "Anna", "Cyril", "Boris", "Anna"];
let mut pocty: HashMap<String, i32> = HashMap::new();
for meno in &mena {
pocty.entry(meno.to_string())
.and_modify(|v| *v += 1)
.or_insert(1);
}
// {"Anna": 3, "Boris": 2, "Cyril": 1}
8b
let mut produkty: HashMap<String, (f64, u32)> = HashMap::new();
produkty.entry("mlieko".to_string())
.and_modify(|(_, pocet)| *pocet += 1)
.or_insert((10.0, 1));
9a
fn najdrahsi(mapa: &HashMap<String, f64>) -> Option<&String> {
mapa.iter()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
.map(|(nazov, _)| nazov)
}
9b
fn velke_mesta(mapa: &HashMap<String, u32>) -> Vec<&String> {
mapa.iter()
.filter(|(_, pop)| **pop > 100_000)
.map(|(mesto, _)| mesto)
.collect()
}
9c
fn celkovy_pocet(mapa: &HashMap<String, Vec<String>>) -> usize {
mapa.values().map(|v| v.len()).sum()
}
10a
fn pocet_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, usize> {
let mut mapa = HashMap::new();
for z in zamestnanci {
*mapa.entry(z.oddelenie.clone()).or_insert(0) += 1;
}
mapa
}
10b
fn celkovy_plat_na_oddeleni(zamestnanci: &[Zamestnanec]) -> HashMap<String, u32> {
let mut mapa: HashMap<String, u32> = HashMap::new();
for z in zamestnanci {
*mapa.entry(z.oddelenie.clone()).or_insert(0) += z.plat;
}
mapa
}
10c
fn vypis_statistiky(zamestnanci: &[Zamestnanec]) {
let pocty = pocet_na_oddeleni(zamestnanci);
let platy = celkovy_plat_na_oddeleni(zamestnanci);
for (oddelenie, pocet) in &pocty {
let plat = platy.get(oddelenie).unwrap_or(&0);
println!("{}: {} ľudí, celkový plat: {}", oddelenie, pocet, plat);
}
}
10d
fn oddelenie_s_najvyssim_platom(zamestnanci: &[Zamestnanec]) -> Option<String> {
let platy = celkovy_plat_na_oddeleni(zamestnanci);
platy.into_iter()
.max_by_key(|(_, plat)| *plat)
.map(|(oddelenie, _)| oddelenie)
}