Mapa

Podstawowe operacje

  1. Mapę tworzymy za pomocą metody new dla typu HashMap
    let mut map = HashMap::new();
  2. Dodawanie i usuwanie elementów do mapy za pomocą metod insert i remove:
    map.insert(String::from("world"), 1);
    map.insert(String::from("rust"), 2);
     
    map.remove("world");
  3. Pobieranie wartości za pomocą metody get:
    let cnt : Option<&i32> = map.get("rust"); // returns an optional reference to a value in the map
  4. Iteracja po elementach mapy (parach klucz-wartość) odbywa się według zasad omówionych przy okazji wektora:
    for (text, number) in &map { // immutable borrow 
       println!("{} has {} occurrences", text, number);
    }

Ćwiczenie - statystyka wyrazów

  1. Napisz funkcję words_stat, która przyjmuje listę wyrazów i zwraca mapę, która posiada statystykę wystąpień poszczególnych wyrazów.
    fn words_stats(words: &Vec<String>) -> HashMap<String, i32> {
        todo!();
    }
  2. Sprawdź statystyki utworu Pan Tadeusz z wykorzystaniem następującego kodu:
    fn hash_map_words_stats_poem() {
        let response = reqwest::blocking::get("https://wolnelektury.pl/media/book/txt/pan-tadeusz.txt").expect("Cannot get poem from a given URL");
        let poem = response.text().unwrap();
     
        let stats = words_stats(&split_to_words(&poem));
        let sorted_stats = sort_stats(&stats);
        println!("{:?}", &sorted_stats[..20]);
    }
     
     
    fn split_to_words(s: &str) -> Vec<String> {
        let mut words = Vec::new();
        for word in s.split_whitespace() {
            let unified_word = word.trim_matches(|c| char::is_ascii_punctuation(&c)).to_lowercase();
            words.push(unified_word);
        }
        words
    }
     
    fn sort_stats(stats : &HashMap<String, i32>) -> Vec<(&str, i32)> {
        let mut sorted_stats : Vec<(&str, i32)> = Vec::new();
        for (word, count) in stats.iter() {
            sorted_stats.push((word, *count));
        }
     
        sorted_stats.sort_by(|(_, c1), (_, c2)| c2.partial_cmp(c1).expect("FAILED"));
     
        sorted_stats
    }

Typ Entry

  1. Rust definiuje typ Entry, który jest widokiem na elementy w mapie. Reprezentuje on element posiadający wartość (Occupied) oraz element pusty (Vacant):
    pub enum Entry<'a, K: 'a, V: 'a> {
        Occupied(OccupiedEntry<'a, K, V>),
        Vacant(VacantEntry<'a, K, V>),
    }
  2. Aby uzyskać widok Entry dla danego klucza, wykorzystujemy metodę entry:
    let entry = map.entry("rust");
  3. Typ Entry definiuje szereg metod, które pozwalają w przyjazny sposób obsłużyć sytuacje, w których nie jesteśmy pewni czy dana wartość istnieje w mapie czy nie. Przykładem jest tutaj metoda or_insert, a poniżej jej zastosowanie w funkcji zliczającej wyrazy:
    let count : &i32 = map.entry(word.to_owned()).or_insert(0);
    *count += 1;
  4. Metoda or_insert zwraca mutowalną pożyczkę, a więc może być wykorzystana po lewej stronie przypisania, wtedy nasz kod wygląda jeszcze krócej:
    map.entry(word.to_owned()).or_insert(0) += 1;
  5. Inna metoda and_modify pozwala z wykorzystaniem domknięć (o których będzie za chwilę) zdefiniować przepis na modyfikację elementu, zanim nastąpi ewentualne dodanie nowych wartości. Skutek wykonania poniższego kodu jest taki sam jak w poprzednim przykładzie:
    map.entry(word.to_owned()).and_modify(|counter| *counter += 1).or_insert(1);

Ćwiczenie

  1. Zmodyfikuj przykład liczenia wyrazów, tak aby wykorzystać typ Entry.