Question about C++ Iterator `map` - c++

In below codes,
std::map<Key,Int>::iterator p;
p = randomTable[chunk].find(value);
if (p != randomTable[chunk].end()) {
} else {
}
How does the p != randomTable[chunk].end() works?
Does this mean, for all elements in randomTable that is not the last (end()) element?

find(value) will return the same as end() if value is not in the map. So if value is in the map, you'll go into the "if" branch, and if not, you'll go into the "else" branch.
end() isn't actually a member of the map - it points to the "element after the end". This is a slightly odd idea, but it probably helps to think that the search has looked at all of the elements in the map and failed to find the one you're looking for.

No. In the documentation for std::map<K, V>::find, you can see:
Return Value
An iterator to the element, if the specified key value is found, or map::end if the specified key is not found in the container.
So, that if statement is just checking that you actually found something.

Related

C++ loop on map not detecting change of map`s end

I am having a problem while looping thru a map (std::map).
Inside my loop, there is a call to a function which sometimes (not always) erases elements of this same map. After this function is used, there is some code which is using some of this map information as input.
I am having no problems after this function erases any elements, except on the unique case that the last element of the map is erased.
My loop semms not to understand that the last element of the map is not the same as when it started to operate, and will try to operate on elements which doesnt exist, creating a crash.
It seems to me that the myMap.end() call on the loop description is not able to update itself with the new end() of the map.
The relevant part of the code is listed below:
for(std::map<int, ConnectionInfo>::iterator kv = myMap.begin(); kv != myMap.end(); ++kv) {
int thisConnectionID=kv->first; //This is where I get garbage when the loop enters when it shouldnt;
ConnectionInfo currentConnectionInfo=kv->second; //This is where I get garbage when the loop enters when it shouldnt;
status=eraseSomeMapElementsIfNecessary(thisConnectionID,currentConnectionInfo.DownPacket); //this function might erase elements on myMap. This generates no problems afterwards, except when the end element of myMap is erased
... //Next parts of the code make no further usage of myMaps, so I just hid it not to pollute the code
}
Is my interpretation that the kv != myMap.end() is not being able to understand that the inner loop is changing (erasing) the last element (end) of myMap?
In this case, how can I fix this issue?
Or is my interpretation wrong and the solution has nothing to do with what I stated before?
Thanks for your help!
The usual idiom when iterating a map with possibly deleting element is:
for(auto it = map.begin(); it != map.end(); ) {
if ( *it == /*is to delete*/ ) {
it = map.erase(it);
}
else
++it;
}
if your eraseSomeMapElementsIfNecessary might erase some random values in map being iterated then this will for sure cause problems. If element to which it is referencing was erased, it becomes invalid, then incrementing it with ++it is also invalid.
The problem is actually only with the it iterator, if eraseSomeMapElementsIfNecessary erases it and then you use it - you have Undefined Behaviour (UB). So the solution is to pass current iterator to eraseSomeMapElementsIfNecessary, and return from it the next one to iterate:
it = eraseSomeMapElementsIfNecessary(it);
the body of the for loop from my example should be inside your eraseSomeMapElementsIfNecessary function. At least this is one solution.
I am having no problems after this function erases any elements, except on the unique case that the last element of the map is erased.
Erasing an element in any container invalidates the iterator to it. After that you increment the invalidated iterator.
You should increment the iterator before you delete the element pointed by it.
If you do not know what elements that function inside the loop erases assume that all iterators are invalidated.
Maybe these 2 links will help:
How can I delete elements of a std::map with an iterator?
https://stackoverflow.com/a/8234813/3464942
Basically, what it all boils down to, is that you must update the iterator before it becomes invalid.
You have to preserve the next iterator before erasing the current one; since the current one will be invalid after deleting the element.
auto nextit = it+1;
map.erase(it);
it = nextit;

modify value of key in boost unordered_map

I have a boost::unordered_map which I want to modify the value of a particular key.
I have seen the question here.
what makes my question different is that in my map key is a simple int and my value is std::vector. I want to update this value by inserting a new PoitCoord at the second position of the vector.
One solution is like that:
auto it = map.find(key);
if(it != map.end())
{
std::vector<PointCoord> pntcrds = it->second;
pntcrds.insert((pntcrds.begin()+1), new_value);
it->second = pntcrds;
}
I am wondering if there is less verbose solution.
The map does not have anything to do with your insertion, if I understand your question correctly. You are only modifying a vector which happens to be stored in a map. You are not modifying the key of a map, but one of it's values.
So the short solution would be:
auto it = map.find(key);
if(it != map.end() && !it->second.empty() )
{
it->second.insert( (pntcrds.begin()+1), new_value);
}
If you know that the key exists in your map, you can shorten this to:
std::vector<PointCords> & pntCords = map[key];
if( ! pntCords.empty() )
pntCords.insert( pntCords.begin()+1, new_value );
NB: If you use the second method and the key does not yet exist, a default constructed (=empty) std::vector<PointCords> will be inserted into the map.
You have to find the key iteration position then update directly the found key by
it->second.insert( (pntcrds.begin()+1), new_value);
but you have to be sure that you've found the iteration and as #Johannes said your vector is not empty.
There is indeed a very simple solution, that covers all the scenarios you might think about. And it is
myMap[key].insert(pntcrds.begin()+1);
If the key does not exist, it will be inserted. otherwise, the value will be updated to the new one;
But you must make sure you have at least one element in your vector. Otherwise, it will crash.
A similar trick would be
myMap[key].push_back(new_value); // appends to the end of the vector

How to find the second to last element in a vector in C++?

I am trying to build a program that uses the second to last element in a vector, so far I've used:
(arr2.rbegin()+1)
If I use a comparison operator in a conditional such as:
if(arr2.rbegin()+1 == true)
I get an error message: no match for operator ==
Many of the answers and comments have the right idea but really ugly syntax. Here are two nice ways to express that.
arr2.end()[-2] // end() is past the last element, -1 for last element, -2 for second-last
arr2.rbegin()[1] // rbegin() is reverse order starting at 0 for last element, 1 for second-last
Demo: http://ideone.com/2cZeUq
It works because RandomAccessIterator, which vector has, is required to provide operator[] such that it[n] is equivalent to *(it + n), just like for pointers.
So the code in your question becomes just
if (arr2.rbegin()[1]) // test penultimate element
looking at the documentation here
http://www.cplusplus.com/reference/vector/vector/?kw=vector
I'd expect you to access your element by
secondToLast = myVector[myVector.size() - 2];
You can try doing like this:-
if(*(arr2.rbegin()+1))
Sometimes there might be less than 2 items in the list, so myVector.size() - 2 or other direct accessors will throw an error. I've done the following . . .
if (myVector.size() > 1)
{
secondToLast = myVector[myVector.size() - 2];
}
It depends on what you mean by "second to last element". Take the following iterator definition...
vector<int>::iterator it = arr2.end();
it--;
it--;
You have to decriment the iterator twice because when you declare the iterator to "point" to the end, it actually references the location AFTER the last element in the vector.
Dont forget that when you want the value that the iterator points to, you have to dereference it. like so...
cout << *it;
Mostly for lulz, but if your elements are non-scalar and you need to access a member of the element in question, you can use the ++-> construction:
std::vector<std::pair<int, int>> arr = ...;
auto grug = arr.rbegin()[1].first;
auto leet = arr.rbegin()++->first;
assert(grug == leet);
The way it works is we post-increment the iterator returned by rbegin() with ++ and then access it with ->. It is actually superior to the clearest [1] form in the sense it will work on any iterator, not only random access iterator.
Post it on review and get some popcorn.
There are many ways you can access elements from the back
one you can use is the back property that comes with std::vector container
and if you want to access an element from the back (either last element or up to n)
you can do this
std::vector vec{1,2,3};
int lastsecond = vec.back()-1; will give you -> 2;
you can check vector properties which there is a decent bit.
https://en.cppreference.com/w/cpp/container/vector

c++: popping an element by key out of an std::map

I'm interested in removing an element with a specific key out of a map and use this element.
Something that will look like:
itr = MyMap.pop(wantedKey);
//Now MyMap is missing the element which has the key 'wantedKey'.
//Do something with this element through 'itr'.
Is there an stl map method for doing this?
EDIT
Following carleeto's response, I want to clarify: What I need is the element being removed from the map and the program being able to use it afterwards, it could be the element itself as a pair, not necessarily an iterator.
There are two options: use it in-place then remove it, or move it to a local variable, remove the entry, then use it.
// use-remove
auto i = MyMap.find(wantedKey);
if (i != MyMap.end()) {
// use-remove
use(i->second);
MyMap.erase(i);
// or
// move-remove-use
auto x = std::move(i->second);
MyMap.erase(i);
use(x);
} else {
// Not found
}
Not that I know of, but you can use std::map::find to get an iterator and then call std::map::erase with said iterator as an argument when you're done.
From your variable naming, I think you might be confusing concepts here.
itr = MyMap.pop(wantedKey);
//Do something with this element through 'itr'.
Iterators only point to elements in containers. Therefore, if you had received an iterator through a function called pop (even if it existed), the iterator would reference not the element you popped, but probably the one after or before it, like std::vector::erase. This is because the purpose of an iterator is to iterate over the elements in a container. Therefore, if an element is not in the container, you cannot get an iterator to it. However, even if you used the iterator returned by the erase function, it would not reference you would be expecting it to.
So you can erase an element from the map, like so many have pointed out, by searching for it, getting the ierator to it and then calling erase with that iterator. but you cannot get an iterator that points to element you have erased. Hope this clears things up.
UPDATE: If all you want is to access the element and use it, then all you need to do use std::map::find to get an iterator and std::map::erase to remove the item from the map, once you have finished using the iterator. The reason is that even if you have stored a copy of the iterator for future use, once you call erase, it will be invalidated. To be able to access it after you have erased it, depending on scope, you will probably need to copy it.
Finally, what you want to do is a very common task - look up a map based on a key and perform an operation on the associated element. It's quite likely that you have a list of keys to go through. You should also look up functors, std::for_each and std::transform. I realise this is not operating on the element after you have removed it, but I thought I would add it in, seeing as how its a related operation. For example: You could move all elements that match a list of keys into another container (say, a vector, and then use the above to operate on them).
Probably what you want to do is
itr = MyMap.find('thing in a string');
to find the iterator and then use it,
MyMap.erase(itr)
And then erase it.
Pop() belongs to the stack datastructure. To access an element of a map, use the [] operator (http://www.cplusplus.com/reference/map/map/operator%5B%5D/), to remove it from the map use (http://www.cplusplus.com/reference/map/map/erase/).
itr = MyMap.find(wantedKey);
if(itr != MyMap.end()) {
use( itr->second );
MyMap.erase(itr);
}
your.uncle = bob;
Using C++'s std::map<T, U>::find():
map.erase(map.find(key));
The way I did it is below. In my case the map stores std::shared_ptr values, making the copy cheap(ish), and the object ownership transferral clear.
auto it = MyMap.find( wantedkey );
if ( it == MyMap.end() ) throw runtime_error("not found");
auto ret = it->second; // make copy of shared_ptr
MyMap.erase(it);
return ret;
The caller gets a shared_ptr with a reference count of at least one (from the copy). Note the function must return the shared_ptr by value, etc.

Erase final member of std::set

How can I delete the last member from a set?
For example:
set<int> setInt;
setInt.insert(1);
setInt.insert(4);
setInt.insert(3);
setInt.insert(2);
How can I delete 4 from setInt? I tried something like:
setInt.erase(setInt.rbegin());
but I received an error.
in C++11
setInt.erase(std::prev(setInt.end()));
You can decide how you want to handle cases where the set is empty.
if (!setInt.empty()) {
std::set<int>::iterator it = setInt.end();
--it;
setInt.erase(it);
}
By the way, if you're doing this a lot (adding things to a set in arbitrary order and then removing the top element), you could also take a look at std::priority_queue, see whether that suits your usage.
Edit: You should use std::prev as shown in Benjamin's better answer instead of the older style suggested in this answer.
I'd propose using a different name for rbegin which has a proper type:
setInt.erase(--setInt.end());
Assuming you checked that setInt is not empty!
Btw. this works because you can call the mutating decrement operator on a temporary (of type std::set<int>::iterator). This temporary will then be passed to the erase function.
A bit less performant, but an alternative option:
setInt.erase(*setInt.rbegin());
If you want to delete 4 instead of the last you should use the find method.
Depending on the use case 4 might not be the last.
std::set<int>::iterator it = setInt.find(4);
if(it != setInt.end()) {
setInt.erase(it);
}
If you want to delete the last element use:
if (!setInt.empty()) {
setInt.erase(--setInt.rbegin().base());
// line above is equal to
// setInt.erase(--setInt.end());
}
While I was not sure if --*.end(); is O.K. I did some reading.
So the -- on rbegin().base() leads to the same result as -- on end().
And both should work.
Check if the set is empty or not. If not, then get the last element and set that as iterator and reduce that iterator and erase the last element.
if (!setInt.empty())
{
std::set<int>::iterator it = setInt.end();
--it;
if(it != setInt.end()) {
setInt.erase(it);
}
}