Can I make the following assumption when map key is not found? - c++

std::map<std::string, int> m;
// Can I make assumption that m["NoSuchKey"] will return 0?
std::cout << m["NoSuchKey"] << std::endl;

Yes. When an item is accessed through operator[] that does not exist, it is created with a default-constructed value, and returned.
For numeric types, default-constructed means 0.

Yes. If the map key is not found, a default value will be inserted.
Specifically, operator[] is understood as:
(*((m.insert(value_type(k, data_type()))).first)).second
Translated to your case, m["NoSuchKey"] means
std::pair<std::string, int> pair_to_insert ("NoSuchKey", 0);
// default value of int is 0.
std::pair<std::map<std::string, int>::iterator, bool>
insert_res = m.insert(value_to_insert);
std::map<std::string, int>::iterator iter_of_inserted_pair = insert_res.first;
std::pair<std::string, int> inserted_pair = *iter_of_inserted_pair;
int inserted_value = inserted_pair.second;
return inserted_value;
In particular, m.insert returns the old key-value pair if the key already exists, and the new key-value pair if not. Therefore, you'll get the expected value if the key already exists, and 0 (the default value) if not.

Related

How to get pair from a map using key in C++

I have the following map:
std::map<char, std::pair<int, int> > robots;
I am using this function to populate the map given the input meets certain conditions:
bool World::addRobot(int row, int col, char robot_name) {
// This if block checks if the desired location is a valid 1 and keeps a track of all the robots already in the grid
if (map_[row][col] == '1' && robots.find(robot_name) == robots.end()){
map_[row][col] = robot_name;
robots.insert(make_pair(robot_name, std::make_pair(row, col)));
}
else{std::cout << "Invalid input" << std::endl;}
return true;
}
Each robot name, which is just a single char, is saved with a pair of its location, which are just row/col coordinates. In the following function, I want to be able to retrieve & ethe location pairs given the robot name:
std::pair<int, int> World::getRobot(char robot_name) {
std::pair<int, int> location = robots.find(robot_name);
return location;
}
But the name location is redlines with the following error message:
No viable conversion from 'std::map<char, std::pair<int, int>>::iterator' (aka '_Rb_tree_iterator<std::pair<const char, std::pair<int, int>>>') to 'std::pair<int, int>'
Where am I going wrong? How can I return the coordinate pairs from just the robot name?
An iterator for a map "points to" a std::pair<const KEY, VALUE>.
For your map, the KEY is char and the VALUE is std::pair<int, int>
So in your code, instead of:
std::pair<int, int> location = robots.find(robot_name);
you need to:
std::pair<int, int> location = robots.find(robot_name)->second;
Also, you need to check and see if the call to find fails to find the key you want. In that case the iterator will be equal to robots.end, and you'll have to deal with that:
const auto it = robots.find(robot_name);
if (it != robots.end()) {
return it->second;
} else {
// Not found, do something else
}
std::map::find returns a map iterator which is a "logical pointer" to a std::pair of key and value of the element in the map (not only the value).
The second member of this pair is the value that you seek to return from getRobot (which is by itself a pair of ints).
Fixed version:
std::pair<int, int> World::getRobot(char robot_name)
{
auto it = robots.find(robot_name);
if (it == robots.end())
{
return std::pair<int, int>(0, 0); // return some default value
}
return it->second;
}
2 additional notes:
I used auto which is very convenient when using iterators (instead of specifying the long iterator type).
I added a check whether the key is in the map. If not - return some default value. You can change the default value, or change the method prototype to return an error status in this case.

How are hash maps implemented in c++?

How is mymap[key]++ visualized. Am I incrementing the key or the value? In LINE 1 hash[numberToFind] is the key or the value that I am inserting into the result?
unordered_map<int, int> hash;
vector<int> result;
for (int i = 0; i < arr.size(); i++) {
int numberToFind = target - arr[i];
if (hash.find(numberToFind) != hash.end()) {
result.push_back(hash[numberToFind]); //LINE 1
result.push_back(i);
return result;
}
Consider going to a good reference before asking on stackoverflow.com. cppreference.com is pretty good.
https://en.cppreference.com/w/cpp/container/unordered_map
key is the key.
mymap[key] returns a reference to the element in mymap at key key.
If key is not yet in mymap, mymap[key] default constructs an element at key and then returns a reference to it.
mymap[key]++ increments the element in mymap at key key. The rule from above still holds: if key is not yet in mymap, then an element is default constructed in mymap at key key, and then incremented.
myvec.push_back(mymap[key]) creates a copy of the value at key, and adds it to the end of myvec. The copying a a function of how push_back works. push_back only adds copies of elements to the end of the vector. The rule from above still holds: if key is not yet in mymap, then an element is default constructed in mymap at key key, and then copied into myvec.
numbers[i] is a reference to the ith element in std::vector<int> numbers. Vectors do not default create elements at an index if there's not already an element at that index. Instead, accessing an out of bounds index is undefined behavior.
hash[x] is a reference to the element in std::unordered_map<int, int> hash at key x. Default creates, as above, if necessary.
hash[numbers[i]] is a reference to the element in hash at key numbers[i]. Default creates as above, if necessary.
hash[numbers[i]] = i; sets the element in hash at key numbers[i] to the value of i.

C++ Structured bindings question regarding unordered_map

I am learning new C++17 features and I came across this:
std::vector<int> nums = { 1, 1, 2, 3 };
std::unordered_map<int, size_t> m;
for (int i = 0; i < nums.size(); ++i)
{
const auto& [inserted_entry, inserted_happen] = m.emplace(nums[i], i);
std::cout << inserted_happen << "\n";
}
The result is:
1
0
1
1
What is happening here? I do not understand.
Also what is inserted_entry?
emplace returns a pair of an iterator to inserted element(or already present element) and a bool representing if the insert was successful.
inserted_happen is a bool.
The second insert fails since 1 already exists as key in the map.
emplace returns std::pair<iterator, bool> which then gets "destructured" and 2 bindings get created. inserted_entry is a reference to the iterator part and inserted_happen is a reference to the bool part.
Return value of emplace is a pair.
The first element of the pair is the iterator for the newly inserted element (if insertion was successful) or of an existing element (if insertion failed).
The second part of the returned pair is a bool value (in your case inserted_happen) indicating whether insertion succeeded.
In your case, the second emplace failed as you have already inserted an element with key equal to 1.
If you want to change the value part of the element, you can just try -
m[nums[i]] = i;

How to return a value from std::map<int, std::string> with a uint64_t key type?

Novice question, but I searched for this and couldn't find something clearly solving my issue - apologies if this is obvious.
I have defined a map which looks like this:
map<int, string> testmap = {
{ 0, "a" },
{ 1, "b" },
{ 2, "c" }
}
However, I need to retrieve a value from testmap using a uint64_t value provided by another function.
When I do testmap[my_uint64_t_value] it returns an empty string, so I think this is because it's adding my_uint64_t_value as a key and setting the value to NULL.
This is the same if I set the map type to <uint64_t, string>, at least the way I'm currently defining my keys.
However, is there a way that I either:
convert the uint64_t value to a regular int
define the map as <uint64_t, string>, and be able to define my
keys as the 'correct' type?
It seems like int type conversion isn't that common, is this something that should be avoided?
The reason why you get an empty string is std::map::operator[] returns a reference to the value if and only if it exists, otherwise it performs an insertion. I suspect you have the latter case.
You need to use std::map::find for search.
uint64_t keyToFind = 1;
if (auto iter = testmap.find(keyToFind); iter != testmap.cend())
{
// do something
std::cout << "Found!\n";
}
else { std::cout << "Not Found!\n"; }
Like #Rene mentioned in the comments, casting from uint64_t to int can cause overflow. Therefore, making the key to larger type(as per requirement) would be a good idea.
std::map<uint64_t, std::string> testmap;
As said in another answer, the [] operator of the map class will perform an insertion with a default-constructed value if the key is not present in the map.
You can first use the count method to determine if the key is present in the map before accessing it.
if(testmap.count(keyToFind))
return testmap[keyToFind];
else
report_key_not_found();
An alternative solution is to use the at method to access the value. It will throw an std::out_of_range exception if the key is not present instead of inserting a new key.

Inserting into a std::map and manipulating the obtained iterator to the inserted element

I know that, if I insert a value into a std::map I can obtain an iterator referring to the inserted element (or the element which was previously there) by checking inserts return value like so:
std::map<int, char> m;
auto ret = m.insert(std::make_pair(1, 'A'));
if (ret.second)
std::cout << "it worked" << std::endl;
// ... now iterations over the map starting at ret.first
However, I was wondering whether it is legal to manipulate the obtained iterator afterwards, e.g. assign the desired value in the case of a failure.
std::map<int, char> m;
auto ret = m.insert(std::make_pair(1, 'A'));
if (!ret.second)
ret.first->second = 'A'; // assign the value in case it went wrong
I noticed that this seems to work, but I am not sure whether this is the desired behaviour since everything I found in case of an failed insertion was to use the operator[] instead. However this would not be a solution for me, because I need the iterator returned by insert afterwards and I can't use insert and the operator[] because of performance reasons.
Long story short: Is it valid to manipulate the data referenced by an iterator returned from std::maps insert()?
Long story short: Is is valid to manipulate the data referenced by an iterator returned from std::maps insert()?
Yes, this is just fine. You cannot modify the key as that is const but you can modify the value the key is mapped to as much as you want.