Finding frequency of an elements in the vector

    let some_vec: Vec<i32> = vec![5, 5, 5, 6, 23, 23, 234, 64, 7, 76, 2, 67, 2, 4, 4, 4];
    let mut freq_vec: HashMap<i32, u32> = HashMap::new();

    for i in &some_vec {
        // println!("{i}");

        let freq: &mut u32 = freq_vec.entry(*i).or_insert(0);
        *freq += 1;
        println!("{:?}", freq)
    }
    println!("{:?}", freq_vec);

I can't to understand *freq += 1; part of code how it update frequency.

The entry API lets you do a bunch of useful things with a key to a hash map. In this case or_insert inserts a default value of 0 if there was no value at that key in the map, and then returns a mutable reference to the value at that key, whether it used the default value or not.

1 Like

Sorry i am very new at rust.. I don't understand. :sweat_smile: :sweat_smile:

Have you worked with languages where you have to deal with references or pointers before? If not, you may want to read up on ownership and references. Later on in the book, updating a hash is discussed, including an explanation of what the *freq (or *count) is doing.

In brief,

  • a &mut T is a reference some T you can override or otherwise modify
  • freq is a &mut u32 like the annotation says, so it references a u32 somewhere (inside the map's allocated memory, as it happens)
  • *freq is the memory of the u32 itself
  • *freq += 1 adds 1 to the u32 in-place (inside the map's allocated memory)

Reading the book is a good way to start learning the language in general.


If you know all that, then this is just a question of understanding the API. While it can be slow to look things up one by one when you're starting out, the std docs are generally quite good. So let's take a look:

        //        vvvvvvvv annotation
        let freq: &mut u32 = freq_vec // HashMap (with an unintuitive name)
            .entry(*i)                // some Thing
            .or_insert(0);            // &mut u32, from annotation

HashMap::entry gives us a HashMap::Entry:

pub enum Entry<'a, K: 'a, V: 'a> {
    Occupied(OccupiedEntry<'a, K, V>),
    Vacant(VacantEntry<'a, K, V>),
}

Ignoring all the stuff in <...>, we can see that it's either occupied or vacant. This is the thing ("some Thing") we called .or_insert on:

Ensures a value is in the entry by inserting the default if empty, and returns a mutable reference to the value in the entry.

So afterwards we have a &mut u32 directly into the memory that the HashMap owns, associated with the key *i. If it was already in the map, that's what's being referenced. If it wasn't in the map before, it was inserted with the value 0, because that's what or_insert(0) does if the Entry is Vacant.

The Entry API, as it's called, is useful for avoiding double-lookups and the like. Contrast:

    for i in &some_vec {
        let freq = if let Some(freq) = freq_vec.get_mut(i) {
            *freq += 1;
            freq
        } else {
            freq_vec.insert(*i, 1);
            freq_vec.get_mut(i).unwrap()
        };

        println!("{:?}", freq)
    }

(It should probably be called freq_map, not freq_vec.)

2 Likes

We get that. It'd be helpful to state which part, though. It's much easier for us to focus on the specific thing you don't understand instead of having to assume that eg. you have never seen a hash table before.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.