How can I efficiently and idiomatically set a value in an unordered_map and find out if a new key was added:
#include <unordered_map>
#include <string>
int main() {
auto map = std::unordered_map<std::string, int>{{"foo", 1}, {"bar", 2}};
map["foo"] = 3;
// how to find out if a new key was added?
}
I can't use insert() directly because I want to overwrite the value if there is one already and insert does not do that. I can't use operator[] directly because it provides no information about whether a new key was added.
I want to avoid doing two searches in the map for performance reasons.
One trick I've seen elsewhere is to get a reference and check if that value is default constructed:
auto& value = map["foo"];
if(value == 0) {
// am inserting a new key
}
value = 3;
But I can genuinely have default constructed values in my map so a default constructed value is not a good indication of new key.
The best I can come up with so far is:
auto size_before = map.size();
map["foo"] = 3;
if (map.size() > size_before) {
// am inserting a new key
}
which seems ugly and it assumes that getting the size of an unordered_map is cheap (is it?).
It looks like unordered_map::insert_or_assign might be the answer to my prayers but sadly it comes in C++17 so I probably wont be able to use it for another 5 years or so. It seems like quite a common thing to want to do so I assume there must be a reasonable way of doing it currently.
You may use std::unordered_map::insert and testing result.
With helper function:
template<typename Map, typename T>
std::pair<typename Map::iterator, bool>
insert_or_assign(Map& m, const typename Map::key_type& k, const T& t)
{
auto p = m.insert({k, t});
if (!p.second) {
// overwrite previous value
p.first->second = t;
}
return p;
}
and then
auto p = insert_or_assign(map, "foo", 3);
if (p.second) {
// inserted
} else {
// assigned
}
Live Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With