Reputation: 2556
Java's getOrDefault was a nice construct for one line access to a map value or the starting point if one does not exist. I do not see anything in the map reference in C++ with a parallel. Does something exist or is it build your own?
I have objects in the map that I would update if they exist, but construct new if they do not. With getOrDefault, I could construct the object on the default side, or access it if it exists.
http://www.cplusplus.com/reference/map/map/
https://www.geeksforgeeks.org/hashmap-getordefaultkey-defaultvalue-method-in-java-with-examples/
Upvotes: 10
Views: 10843
Reputation: 1610
The answer of L.F is using map::emplace
is the way to go if the map's value type does not require a constructor, as each time the constructor is called as shown in the example below.
C++17 comes with insert_or_assign
which is slightly different.
Probably the most efficient solution is to do something like this:
template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
auto p = map.lower_bound(k);
if (p != map.end()) {
return std::make_pair(p, false);
}
return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}
Here is an example that shows how it works:
#include <sstream>
#include <iostream>
#include <string>
#include <map>
class Element {
public:
Element(int value) : value(value) {
std::cout << "Element ctor value = " + std::to_string(value) << std::endl;
}
int value;
};
template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
auto p = map.lower_bound(k);
if (p != map.end()) {
return std::make_pair(p, false);
}
return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}
int main(int argc, char **argv) {
std::map<int, Element> map;
auto& e1 = *map.emplace(1, Element(1)).first;
std::cout << "Element in map: " << std::to_string(e1.second.value) << std::endl;
auto& e11 = *map.emplace(1, Element(11)).first;
std::cout << "Element in map: " << std::to_string(e11.second.value) << std::endl;
auto e2 = *map.insert_or_assign(2, 2).first;
std::cout << "Element in map: " << std::to_string(e2.second.value) << std::endl;
auto e22 = *map.insert_or_assign(2, 22).first;
std::cout << "Element in map: " << std::to_string(e22.second.value) << std::endl;
auto e3 = *insert_or_create(map, 3, 3).first;
std::cout << "Element in map: " << std::to_string(e3.second.value) << std::endl;
auto e33 = *insert_or_create(map, 3, 33).first;
std::cout << "Element in map: " << std::to_string(e33.second.value) << std::endl;
}
which produces
Element ctor value = 1
Element in map: 1
Element ctor value = 11 <-- calling ctor, not optimized away
Element in map: 1 <-- still old value in map as expected
Element ctor value = 2
Element in map: 2
Element ctor value = 22 <-- calling ctor
Element in map: 22 <-- new value assigned to key 2, as expected
Element ctor value = 3
Element in map: 3 <-- ctor not called as wanted!!!!!!
Element in map: 3
This shows that insert_or_create
does not call the constructor of Element
.
I am not sure why such a function is not in the std::map interface as it is pretty useful.
Upvotes: 1
Reputation: 75
I think below is what you are looking for
mymap.emplace(key, DEFAULT_VALUE).first->second = value;
like used in below sample
#define DEFAULT_VALUE 0
map<int,int> mymap;
mymap[1] = 1;
// mymap is having some entries.
// will increment existing element.
mymap.emplace(1,DEFAULT_VALUE).first->second++;
// will insert a new element with key 2 and value as default(0), than increment it.
mymap.emplace(2,DEFAULT_VALUE).first->second++;
Upvotes: 0
Reputation: 127
map.put(sum, map.getOrDefault(value, 0) + 1);
auto it1 = map.find(value);
if (it1 != um.end())
map[value]++;
else
map[value] = 1;
Upvotes: 0
Reputation: 51
I got here when I was trying to figure out a way to solve LeetCode Single Number question using HashTable in C++.
There is no direct alternative to getOrDefault which is used in Approach 2 and Java, but I was able to use operator[] to access an element of an unordered_map. See http://www.cplusplus.com/reference/unordered_map/unordered_map/operator[]/
If the key exists in the map, then the operator[] will return the reference to its mapped value.
If the key does not exist, then the operator[] will add the key to the map with a value 0.
This could be useful when you are trying to increment a value of a key if it already exists or add a new key to the map if it does not.
For example, I used the following in C++
for (int i : nums) {
hash_table[i] = hash_table[i] + 1;
}
which is an alternative to the following in Java
for (int i : nums) {
hash_table.put(i, hash_table.getOrDefault(i, 0) + 1);
}
Upvotes: 4
Reputation: 16680
Maybe I'm misunderstanding what you're asking, but that's how map
works.
map<int, string> m;
string s = m[3];
will set s
to a default-constructed string
.
When you use operator[]
to look up an key in a map, it will always hand you back an value. If the key doesn't exist in the map, it will insert it, with a default constructed value.
If you want this behavior, but with a different value (not a default-constructed value), then you could use emplace
, as L.F. has suggested.
Upvotes: 0
Reputation: 20609
I have objects in the map that I would update if they exist, but construct new if they do not. With getOrDefault, I could construct the object on the default side, or access it if it exists.
Use emplace
.
auto& element = *map.emplace(key, value).first;
emplace
inserts a new element if the key is not present, and returns a pair consisting of an iterator to the element (inserted or already existent) and a bool
value indicating whether insertion took place.
Upvotes: 5