Hashmap frequency counter, avoiding a .to_string

  1. I am writing a "word frequency counter". Here is what I have so far:

pub struct WordFreq {
    storage: HashMap<String, usize>,
}

impl WordFreq {
    pub fn new() -> WordFreq {
        WordFreq { storage: HashMap::new() }
    }

    pub fn add_word(&mut self, word: &str, n: usize) {
        let mut hm = &mut self.storage;

        let v = hm.get(word);
        let word = word.to_string();

        match v {
            None => hm.insert(word, n),
            Some(old_val) => hm.insert(word, n+old_val),
        };
    }
}
  1. In this code, we call word.to_string() always. This seems unnecessary in the case when the string already exists in the hashmap. Is there a way to optimize this away?

Use the entry api:

*hm.entry(word).or_insert(0) += 1;

Edit: Dammit, I knew I should have tried this first.

1 Like

The entry API requires a full key (String), but you can do a lookup first with the &str:

    pub fn add_word(&mut self, word: &str, n: usize) {
        let hm = &mut self.storage;
        if let Some(val) = hm.get_mut(word) {
            *val += n;
        } else {
            hm.insert(word.to_string(), n);
        }
    }

You could also consider using &str for the keys, if you don't need the map to outlive the source of your strings, and then entry is easy:

pub struct WordFreq<'a> {
    storage: HashMap<&'a str, usize>,
}

impl<'a> WordFreq<'a> {
    pub fn new() -> WordFreq<'a> {
        WordFreq { storage: HashMap::new() }
    }

    pub fn add_word(&mut self, word: &'a str, n: usize) {
        *self.storage.entry(word).or_insert(0) += n;
    }
}
1 Like