Difficulty with map of map deletion - c++

How do you delete the first element of the inner map of a map of a map?
I've tried doing something like
my_map[here].erase(my_map[here].begin())
but I'm getting unexpected results. Any suggestions or resources would be great.

Does my_map[here] actually exist?
You may want to find it, i.e.
if((auto it = my_map.find(here)) != my_map.end()) {
it->erase(it->begin());
}
If my_map[here] didn't exist when you tried to access it, a new element will be created there:
http://www.cplusplus.com/reference/map/map/operator%5B%5D/
If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value. Notice that this always increases the container size by one, even if no mapped value is assigned to the element (the element is constructed using its default constructor).
To prevent this, you can use the find function as I have indicated above.
find searches for the element with the specified key. If it finds something, it will return the iterator to that element. Otherwise, it will return my_map.end(), which is NOT the last element but a special iterator signifying the end of the structure.

Related

How to change some keys in a std::map using extract and insert

I've recently read this question about node_type, and I learned that if I need to modify the key of a specific node in a map, I could extract the node, modify the key and insert the node back in the map (like in this example).
Now, let's say I need to change the key of multiple nodes of this map, and to know which nodes I need to modify, I have to iterate over the values. I think I shouldn't insert the nodes back in the map while iterating (but maybe I'm wrong, see the end of the question), so I was thinking I could iterate over the map in a first time, extract the nodes I need to modify, store those in a vector, and insert the nodes back in a second time, when I'm finished iterating the map and modifying the nodes.
I suspect I could achieve that by storing the keys in a vector, not the nodes, but since the whole point is to change the keys, I thought it could be handy to use the nodes which seem to be made for that purpose.
So, basically, here's what I have so far:
std::vector</*don't know what to put here since node_type is undefined*/> tmp;
for (auto& it : myMap) {
if(it.second->needsToBeModified()){
tmp.push_back(myMap.extract(it.first));
}
}
I can't figure out what type that tmp vector would be.
As I said before, I may be wrong about not wanting to insert back the nodes while iterating, reading this answer stating that inserting or deleting in a map while iterating doesn't invalidate iterators (apart from the deleted entry). But this feels weird, what would happen if I insert a node in the map while iterating over it, would the inserted node come up in the iterating loop? Could that lead in an infinite loop?
The vector type is simple to address, since std:map specializations expose a type alias.
std::vector<std::map<K,V>::node_type> tmp;
The more involved point is the iteration. You cannot use a simple range-based for. That is because node extraction invalidates iterators to the extracted element. And that includes the iterator used behind the scenes for iteration. Instead, a more appropriate loop would be
for (auto it = myMap.begin() ; it != myMap.end();) {
if(it->second->needsToBeModified()){
auto out = it++;
tmp.push_back(myMap.extract(out));
}
else
++it;
}
Insertion while iterating is possible (since it doesn't invalidate iterators). But if the iteration order is important (even if only for debuggability), I think an auxiliary vector like you use is appropriate.
StoryTeller's answer is correct, however it would be nice if you wouldn't have to know how to spell out the whole type. There are several ways to simplify the declaration of tmp. The first is to avoid repeating the type of myMap, by using decltype():
std::vector<decltype(myMap)::node_type> tmp;
However, this still required you to know that the type of myMap contains another type named node_type. If you didn't know that, and still wanted to declare a vector that holds the result of myMap.extract(), then you can write:
std::vector<decltype(myMap.extract(myMap.begin()->first))> tmp;
As you can see, that's unfortunately quite a bit longer to type in, but you didn't have to use any knowledge about myMap, other than how you are actually using it inside the for-loop.

c++ Should I compare the last element of a list to my val when I am running find algorithm? [duplicate]

I'd like to use the find() method, to catch a particular element of my std::map()
Now the return value is either the element I was looking for, or it it points to the end of the map if the element hasn't been found (C.f.: http://www.cplusplus.com/reference/map/map/find/ ).
So I'm using these two information whether to decide if an element is in the map or not. But what happens if the element is already inside but at the very end ? My if-query would then decide that it's not existing and therefor my program will be made up.
Am I getting this routine right, and if, how to prevent it ?
Thanks for your support
The last element in any container is not the same as the containers end iterator. The end iterator is actually one step beyond the last element in the container.
That means you can rest easily, and use find without worries. If the element you look for is the last element, you will get what you search for.
You can actually use if(!map.count(elem)) to check whether elem is present in the map, count() returns 0 if elem is not present in the map while find() returns an iterator which has to been compared to the end iterator, i.e., if(map.find(elem) != map.end()).

Does find() also return the last element of a map in C++?

I'd like to use the find() method, to catch a particular element of my std::map()
Now the return value is either the element I was looking for, or it it points to the end of the map if the element hasn't been found (C.f.: http://www.cplusplus.com/reference/map/map/find/ ).
So I'm using these two information whether to decide if an element is in the map or not. But what happens if the element is already inside but at the very end ? My if-query would then decide that it's not existing and therefor my program will be made up.
Am I getting this routine right, and if, how to prevent it ?
Thanks for your support
The last element in any container is not the same as the containers end iterator. The end iterator is actually one step beyond the last element in the container.
That means you can rest easily, and use find without worries. If the element you look for is the last element, you will get what you search for.
You can actually use if(!map.count(elem)) to check whether elem is present in the map, count() returns 0 if elem is not present in the map while find() returns an iterator which has to been compared to the end iterator, i.e., if(map.find(elem) != map.end()).

unordered_map C++

I am using unordered map like
unorderedmap.insert(make_pair(5, 6));
How could I get the iterator for the position of the key (value 5)? Do I need to use find() again or could I some how get it from the return value?
Thanks.
Assuming you are using this unordered_map, the iterator is:
unorderedmap.insert(make_pair(5, 6)).first;
First of all unordered_map is not a standard container. But if you are using a unordered_map provided by the compiler which provides an API similar to this then it returns a pair of <iterator,bool>. The iterator part of the pair which can be accesses using .first will give you the location where the element got inserted (if it was inserted succesfully, this can be tested using the bool second).
This is taken from http://www.cplusplus.com/reference/stl/map/insert/
So as stated before, use the pair::first to get the iterator.
The first version returns a pair, with
its member pair::first set to an
iterator pointing to either the newly
inserted element or to the element
that already had its same value in the
map. The pair::second element in the
pair is set to true if a new element
was inserted or false if an element
with the same value existed.
The unordered_map interface is similar to the set interface or the map interface in that the insert method returns a std::pair<iterator,bool>:
the bool is true if the insertion took place, it did not take place if an element with a similar key already existed in the container
the iterator points to the right element: either the already existing one or the newly inserted one depending on the case
Therefore, if you only wish to access the element, you can simply use:
map.insert(std::make_pair(key,value)).first
However do check that you do not need to know if the insert was successful or not, in general one care about it :)

Checking if element exists in unorderd_set before inserting it

I would like to ask if there is any point of checking if element exists in unorderd_set before inserting it? According to documentation:
Each element is inserted only if it is not equivalent to any other element already in the container (elements in an unordered_set have unique values).
So if I'm getting it correctly in my case:
Element *element = new Element;
//...
if ( my_set.find (element) == my_set.end() )
my_set.insert(element);
It is not required - correct? What actually happens if I try to insert element that is already in the set. Does it do exactly the same check that I am doing?
If you try to insert a value that is already present, the set will remain unchanged. The call will return a pair containing an iterator and Boolean. The Boolean will be false if the item was already present.
http://en.cppreference.com/w/cpp/container/unordered_set/insert
Note that in your case where the set contains pointers it will only prevent you from having duplicate pointers, not duplicate contents. For example,
if your Element object represented an element, like Oxygen, it might contain a value for the atomic number and a string for the name. If you only want oxygen to appear once, a set of pointers will not automatically do this.
An insert() on unorderd_set does a find() to see if the object exists and then inserts the object.
So in your code they are two find(), one explicit, and other inside the insert()