Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append to vector as value of hashmap

Tags:

rust

How does one append to a vector that is a hashmap value?

use std::collections::HashMap;

fn main() {
    let mut dict = HashMap::new();
    let mut prefix = ["", ""];

    let lorem = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eum debitis earum quidem dolorem quos consequatur perspiciatis architecto! Reiciendis rem est blanditiis fugiat quidem similique suscipit soluta, ab veniam, laborum numquam. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eum debitis earum quidem dolorem quos consequatur perspiciatis architecto! Reiciendis rem est blanditiis fugiat quidem similique suscipit soluta, ab veniam, laborum numquam.";

    for token in lorem.split_whitespace() {
        if prefix[0] != "" {

            let key = prefix.join(" ");

            if !dict.contains_key(&key) {
                dict.insert(key, vec![token]);
            } else {
                let v = dict.get(&key);
                v.push(token); // does not actually return a vector
            }
        }

        prefix[0] = prefix[1];
        prefix[1] = token;
    }
}

The problem is the line let v = dict.get(&key); Normally I would expect to be able to push onto the vector, but it seems to be returning some kind of collection, which I'm not sure on how to append to.

like image 375
user2758113 Avatar asked Oct 20 '15 18:10

user2758113


Video Answer


1 Answers

Idiomatic solution

The idiomatic solution to your problem would be to use entry, as shown below:

for token in lorem.split_whitespace() {
    if prefix[0] != "" {

        let key = prefix.join(" ");

        match dict.entry(key) {
            Entry::Vacant(e) => { e.insert(vec![token]); },
            Entry::Occupied(mut e) => { e.get_mut().push(token); }
        }
    }

    prefix[0] = prefix[1];
    prefix[1] = token;
}

If the key doesn't exist, you will get a vacant entry that you can use to insert a new value. If it does exist, you will get an occupied entry that you can use to modify the current value. If you want to know more, check out the documentation.

Alternative solution as proposed by Huon in the comments

This one is even shorter and seems better to me once you grasp what is going on:

for token in lorem.split_whitespace() {
    if prefix[0] != "" {
        let key = prefix.join(" ");
        dict.entry(key).or_insert(Vec::new()).push(token);
    }

    prefix[0] = prefix[1];
    prefix[1] = token;
}

Why your code didn't work

get returns an Option<&Vec<&str>>. You would need to take the Vec out of the option, but even in that case you cannot mutate it because it is a shared reference. You can use get_mut in combination with unwrap as shown below (however, this is considered bad style. You should really use entry):

for token in lorem.split_whitespace() {
    if prefix[0] != "" {

        let key = prefix.join(" ");

        if !dict.contains_key(&key) {
            dict.insert(key, vec![token]);
        } else {
            let v = dict.get_mut(&key).unwrap();
            v.push(token);
        }
    }

    prefix[0] = prefix[1];
    prefix[1] = token;
}
like image 171
aochagavia Avatar answered Oct 11 '22 19:10

aochagavia