Home > Mobile >  Is there a more idiomatic way to "insert or accumulate" into an unordered_map representing
Is there a more idiomatic way to "insert or accumulate" into an unordered_map representing

Time:12-15

Consider code like the following:

#include <iostream>
#include <unordered_map>

std::unordered_map<char, int> get_letter_frequencies(const std::string& str) {
    std::unordered_map<char, int> freqs;
    for (char ch : str) {
        auto iter = freqs.find(ch);
        if (iter == freqs.end()) {
            freqs[ch] = 1;
        } else {
            iter->second  ;
        }
    }
    return freqs;
}

int main()
{
    std::string str = "AABBDBCABDA";
    auto freqs = get_letter_frequencies(str);
    std::cout << freqs['B'] << "\n";

    return 0;
}

which stores counts of letters in an unordered_map. My question is is there a snippet of terser/more idiomatic code with which i can replace

auto iter = freqs.find(ch);
if (iter == freqs.end()) {
    freqs[ch] = 1;
} else {
    iter->second  ;
}

I could write a function insert_or_accumulate( ... ) but it seems like overkill.

CodePudding user response:

Just do:

for (char ch : str) {
      freqs[ch];
}

Just accessing freqs[ch] will create the key-value pairing if it's missing, using the default constructor (for int, that makes 0), and returns a reference to the value (new or existing), so freqs[ch] will increment existing values, and both create and increment missing values.

Note: I'm using prefix by preference; it doesn't matter here since we're incrementing a primitive built-in type, but in C you want to get in the habit of using prefix increment by default, as classes overloading increment cannot implement postfix increment as efficiently as prefix increment (postfix requires making a copy of the instance, prefix can operate in place with no copies).

CodePudding user response:

Using a std::unordered_map is a bit overkill itself, given the amount of dynamic allocation and hashing it does. Sure, it's fast and easy to iterate over the non-zero elements, but a simpler way to count characters is with a simple array that holds 256 values.

An array is simpler and most likely faster, at the expense of maybe more hassle to iterate over items if you ever wanted to do that. However, given the indirection that would happen with unordered_map, I personally still prefer this approach over others. It's also ordered.

int freqs[256] = {};
for (unsigned char c : str)   freqs[ch];

As a drop-in for your function that returns a value, you can use std::array.

#include <array>
#include <string>
#include <iostream>

typedef std::array<int, 256> CharFreqType;

CharFreqType get_letter_frequencies(const std::string& str)
{
    CharFreqType freqs{};
    for (unsigned char c : str)   freqs[c];
    return freqs;
}

int main()
{
    auto freqs = get_letter_frequencies("AABBDBCABDA");
    std::cout << freqs['B'] << "\n";
}
  • Related