How to get the last element of an std::unordered_map?
myMap.rbegin() and --myMap.end() are not possible.
There is no "last element" in a container that is unordered.
You might want an ordered container, e.g. std::map and access the last element with mymap.rbegin()->first (Also see this post)
EDIT:
To check if your iterator is going to hit the end, simply increment it (and possibly save it in a temporary) and check it against mymap.end(), or, even cleaner : if (std::next(it) == last)
In your comments, it appears your goal is to determine if you are on the last element when iterating forward. This is a far easier problem to solve than finding the last element:
template<class Range, class Iterator>
bool is_last_element_of( Range const& r, Iterator&& it ) {
using std::end;
if (it == end(r)) return false;
if (std::next(std::forward<Iterator>(it)) == end(r)) return true;
return false;
}
the above should work on any iterable Range (including arrays, std containers, or custom containers).
We check if we are end (in which case, we aren't the last element, and advancing would be illegal).
If we aren't end, we see if std::next of us is end. If so, we are the last element.
Otherwise, we are not.
This will not work on iterators that do not support multiple passes.
You cant. by definition, the element is not stored based on some sort of order. the key is hashed first and that's why O(1) search is possible. if you wanna check whether a key exists in the unordered_map or not, u can use this code:
std::unordered_map dico;
if(dico.count(key)!=0){
//code here
}
std::unordered_map::iterator last_elem;
for (std::unordered_map::iterator iter = myMap.begin(); iter != myMap.end(); iter++)
last_elem = iter;
// use last_elem, which now points to the last element in the map
This will give you the last element in whatever order the map gives them to you.
Edit: You need to use std::unordered_map<YourKeyType, YourValueType> instead of just std::unordered_map. I just wrote it like this because you did not provide the type in your question.
Alternatively, as suggested by vsoftco (thanks), you could declare both last_elem and iter as decltype(myMap)::iterator.
(If you're compiling with the MSVC++ compiler, then you will need to add typedef decltype(myMap) map_type; and then instead of decltype(myMap)::iterator use map_type::iterator.)
.end() is an iterator to the "element past the last element". That's why you compare it like this when you loop through a map:
for (auto it = myMap.begin(); it != myMap.end(); ++it) // '!=' operator here makes it possible to only work with valid elements
{
}
So you want the "last" element (whatever that may be, because it's not really guaranteed to be the last in an unordered map, since it ultimately depends on how the key was hashed and in which "bucket" it ends up in). Then you need: --myMap.end()
More specifically, .end() is a function, that returns an iterator, same as .begin() returns an iterator. Since there is no .rbegin() in an std::unordered_map, you have to use -- (the decrement operator):
auto it = --myMap.end();
To access the key you use it->first, to access the value you use it->second.
The accepted answer seems wrong. Unordered_map does have the last element even though the key-value pair is not stored in sorted order. Since the iterator of unorered_map is forwar_iterator(LegacyForwardIterator), the cost to find the last element is O(n). Yakk - Adam gave the correct answer. Essentially, you have to iterator the container from begin to end. At each iteration, you have to check whether the next element is end(); if yes then you are at the last element.
You cannot call prev(it) or --it. There will be no syntax error, but you will have a runtime error (more likely segmentation fault) when using the prev(it) or --it. Maybe next version of compiler can tell you that you have an logic error.
It may not be the best solution, performance-wise, but in C++11 and later, I use a combination of std::next() and size() to jump all elements from the beginning of the map, as shown below:
std::unordered_map<int,std::string> mapX;
...
if (mapX.size() > 0) {
std::unordered_map<int,std::string>::iterator itLast =
std::next(mapX.begin(), mapX.size() - 1);
...
Related
When we are iterating in reverse direction, I see that most people use the following structure:
for (auto it = vec.rbegin(); it != vec.rend(); it++)
{
// block of code //
}
But for a long time, I have a doubt about using this, and I want to know why the following code does not work.
As we know, the last element will have the highest index than any element index in the array, and the array is going to take contiguous memory.
My primary doubt is when iterating backwards, why shouldn't we use it--?
I want the reason why the following code is not going to work. I am running the loop from rbegin, that is the last element, and I am going until the first element. I am decrementing it by one in every iteration.
for (auto it = vec.rbegin(); it >= vec.begin(); it--)
{
cout << *it << endl;
}
Even the below code is not working, why?
for(auto it = vec.rbegin(); it >= vec.begin(); it++)
{
cout << *it << endl;
}
First of all, in the given codes, the for loop's conditions are making issue due to type-mismatch.
The vec.rbegin() gives the std::vector::reverse_iterator, and the vec.begin() gives the std::vector::iterator; those are different types and can not be compared. Hence, you get compiler errors in those places.
When iterating backwards, why shouldn't we use it--?
See the following reference picture from std::reverse_iterator
When you use rbegin(), you start from the last element. In order to advance further (like every iterator implementation) it uses the operator++. Advance here means, iterating backwards direction, because the starting point is the last element. Therefore, you should be using it++ or ++it instead.
For the last for loop example, however, there is only a type-mismatch issue. Using ✱std::reverse_iterator::base(), you could get/ convert the reverse iterator to the corresponding base iterator, and it can be compared with the vec.begin().
That means the following change will make it work:
for (auto it = vec.rbegin(); it.base() != vec.begin(); ++it)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
std::cout << *it << " ";
}
See a demo
Side Note:
Even though, the above is possible, I would strongly suggest use the same iterators for comparison, which provides the code more natural look, and easy to understand for the fellow devs and less error-prone.
✱Read more: Can I convert a reverse iterator to a forward iterator?
In all, it is just a design issue, the designer designed the begin, rbegin, end, rend in that way.
Take an example of a container with three elements {1,2,3}.
begin() points to 1, end() points to the position after 3
rbegin() points to 3, rend() points to the position before 1.
You can understand rbegin() as a special data struct of a special pointer (aka iterator) such that + operator would be overloaded into -.
You can but not recommended to mix rbegin() with begin() cause they are different things. And mixing is always error-prone for most of the time.
Reverse iterators are designed to mimic forward iterators (and iterators in general are designed to mimic pointers), so algorithms can be written in an agnostic way that works with both types. All iterators advance with operator++ and decrement with operator-- , where
forward iterators advance in a forward direction and decrement in a backwards direction
reverse iterators advance in a backward direction and decrement in a forward direction
I have a sorted vector and want to find a particular element in it. I can use binary_search for this but it only tells if it is present or not. I also need an iterator to access the element. Is there an easy way to this or I have to search it sequentially.
Any help appreciated.
Look into lower_bound and upper_bound. lower_bound gives the iterator to the first matching element while upper_bound gives the iterator one past the last matching element.
If either algorithm fails to find a match, it returns an iterator to the place where the item could be inserted to maintain a sorted container.
I've always felt binary_search was misleadingly named.
std::lower_bound will return the first element that is not less than your value. Meaning if the element returned is equal to your value your good, if it is not equal or the end iterator than the right element hasn't been found.
Here is the code from the dupe
template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
// Finds the lower bound in at most log(last - first) + 1 comparisons
Iter i = std::lower_bound(begin, end, val);
if (i != end && !(val < *i))
return i; // found
else
return end; // not found
}
Remember if you use std::upper_bound than it returns the first greater element so it is not as easy to adapt to your purposes because if your element is indeed found you have to decrement the iterator and even then you still may not find it
According to some STL documentation I found, inserting or deleting elements in an std::list does not invalidate iterators. This means that it is allowed to loop over a list (from begin() to end()), and then add elements using push_front.
E.g., in the following code, I initialize a list with elements a, b and c, then loop over it and perform a push_front of the elements. The result should be cbaabc, which is exactly what I get:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
testList.push_front(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
When I use reverse iterators (loop from rbegin() to rend()) and use push_back, I would expect similar behavior, i.e. a result of abccba. However, I get a different result:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
testList.push_back(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
The result is not abccba, but abcccba. That's right there is one additional c added.
It looks like the first push_back also changes the value of the iterator that was initialized with rbegin(). After the push_back it does not point anymore to the 3rd element in the list (which was previously the last one), but to the 4th element (which is now the last one).
I tested this with both Visual Studio 2010 and with GCC and both return the same result.
Is this an error? Or some strange behavior of reverse iterators that I'm not aware of?
The standard says that iterators and references remain valid during an insert. It doesn't say anything about reverse iterators. :-)
The reverse_iterator returned by rbegin() internally holds the value of end(). After a push_back() this value will obviously not be the same as it was before. I don't think the standard says what it should be. Obvious alternatives include the previous last element of the list, or that it stays at the end if that is a fixed value (like a sentinel node).
Technical details: The value returned by rend() cannot point before begin(), because that is not valid. So it was decided that rend() should contain the value of begin() and all other reverse iterators be shifted one position further. The operator* compensates for this and accesses the correct element anyway.
First paragraph of 24.5.1 Reverse iterators says:
Class template reverse_iterator is an iterator adaptor that iterates from the end of the sequence defined
by its underlying iterator to the beginning of that sequence. The fundamental relation between a reverse
iterator and its corresponding iterator i is established by the identity:
&*(reverse_iterator(i)) == &*(i - 1).
I think to understand this, it's best to start by re-casting the for loop as a while loop:
typedef std::list<std::string> container;
container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
container::reverse_iterator itList = testList.rbegin();
while (itList != testList.rend()) {
testList.push_back(*itList);
++itList;
}
Along with that, we have to understand how a reverse_iterator works in general. Specifically a reverse_iterator really points to the element after the one you get when you dereference it. end() yields an iterator to just after the end of the container -- but for things like arrays, there's no defined way to point to just before the beginning of a container. What C++ does instead is have the iterator start from just after the end, and progress to the beginning, but when you dereference it, you get the element just before where it actually points.
That means your code actually works like this:
After that, you get pretty much what you expect, pushing back B and then A, so you end up with ABCCCBA.
Try using an iterator for both. Try:
std::list<std::string>::iterator i = testList.end();
and reverse through with --i
Perhaps this is a duplicate but I did not find anything searching:
When erase(value) is called on std::multiset all elements with the value found are deleted. The only solution I could think of is:
std::multiset<int>::iterator hit(mySet.find(5));
if (hit!= mySet.end()) mySet.erase(hit);
This is ok but I thought there might be better. Any Ideas ?
auto itr = my_multiset.find(value);
if(itr!=my_multiset.end()){
my_multiset.erase(itr);
}
I would imagine there is a cleaner way of accomplishing the same. But this gets the job done.
Try this one:
multiset<int> s;
s.erase(s.lower_bound(value));
As long as you can ensure that the value exists in the set. That works.
if(my_multiset.find(key)!=my_multiset.end())
my_multiset.erase(my_multiset.equal_range(key).first);
This is the best way i can think of to remove a single instance in a multiset in c++
This worked for me:
multi_set.erase(multi_set.find(val));
if val exists in the multi-set.
I would try the following.
First call equal_range() to find the range of elements that equal to the key.
If the returned range is non-empty, then erase() a range of elements (i.e. the erase() which takes two iterators) where:
the first argument is the iterator to the 2nd element in the returned
range (i.e. one past .first returned) and
the second argument as the returned range pair iterator's .second one.
Edit after reading templatetypedef's (Thanks!) comment:
If one (as opposed to all) duplicate is supposed to be removed: If the pair returned by equal_range() has at least two elements, then erase() the first element by passing the the .first of the returned pair to single iterator version of the erase():
Pseudo-code:
pair<iterator, iterator> pit = mymultiset.equal_range( key );
if( distance( pit.first, pit.second ) >= 2 ) {
mymultiset.erase( pit.first );
}
We can do something like this:
multiset<int>::iterator it, it1;
it = myset.find(value);
it1 = it;
it1++;
myset.erase (it, it1);
Here is a more elegant solution using "if statement with initializer" introduced in C++17:
if(auto it = mySet.find(value); it != mySet.end())
mySet.erase(value);
The advantage of this syntax is that the scope of the iterator it is reduced to this if statement.
Since C++17 (see here):
mySet.extract(val);
auto itr=ms.find(value);
while(*itr==value){
ms.erase(value);
itr=ms.find(value);
}
Try this one It will remove all the duplicates available in the multiset.
In fact, the correct answer is:
my_multiset.erase(my_multiset.find(value));
I was wondering if something like this is safe...
// Iterating through a <list>
while ( iter != seq.end()) {
if ( test ) {
iter = seq.erase( iter );
} else {
++iter;
}
I know that iterating through a vector in this way would invalidate the iterator, but would the same thing occur in a list? I assume not since a list is sequential through pointers rather than being "next" to each other in memory, but any reassurance would be helpful.
This is just fine because the erase method returns a new valid iterator.
Yes -- std::list::erase(): "Invalidates only the iterators and references to the erased elements."
That said, you probably shouldn't do this at all -- you seem to be trying to imitate std::remove_if().
The standard defines erase behaviour for every STL container. For std::list only iterators to the erased elements are invalidated. The return value of erase needn't be a dereferencable one, though (it could be list.end()).
Therefore, to erase all elements in a list the following is absolutely valid:
.. it = l.begin();
while(it != l.end()) {
it = l.erase(it);
}
BUT beware of something like this (dangerous pitfall):
for (.. it = l.begin; it != l.end(); ++it) {
it = l.erase(it);
}
If it is l.end(), it is incremented twice (second time by the loop head). Baamm.
Yes, this is the standard way to do that. See Effective STL, Item 9 (p. 46).
Yes, this is totally safe. The erase() function returns an iterator to the element succeeding the one which was erased. Had you not reassigned the result of erase() to iter, you'd have trouble.
As others have explained, your code does not invalidate the iterator used in the function. However, it does invalidate other iterators if the collection is a vector, but not if the collection is a list.
As others have mentioned, yes, it will work. But I'd recommend using list::remove_if instead, as it's more expressive.