I dont understand what does that piece of code does
static TwoWayHostPair hostpair;
map <TwoWayHostPair, Traffic> mymap;
//here some map element inserted to mymap and hostpair initialized
map <TwoWayHostPair, Traffic>::iterator iter = mymap.begin();
iter = mymap.find(hostpair);
if (iter == mymap.end()) {
iter = mymap.insert(make_pair(hostPair, Traffic())).first; //line8
}
My question is what happens in line8? I didnt get it. Isnt it supposed to be type map<...>:iterator and after this insertion does it stay same type?
std::map::insert return std::pair< iterator,bool >, below statement is correct. second bool return value indicates whether the insertion took place
iter = mymap.insert(make_pair(hostPair, Traffic())).first; //line8
see reference here
As used here,
iter = mymap.insert(make_pair(hostPair, Traffic())).first; //line8
mymap.insert returns a pair<iterator,bool>. first, then, accesses the iterator.
insert(make_pair......
make_pair is used to insert pair value to a map.You have iterated for all the element in map having hostpair.
EDIT
Refer cplusplus map to know more.
iter = mymap.find(hostpair);
if (iter == mymap.end()) {
iter = mymap.insert(make_pair(hostPair, Traffic())).first; //line8
}
The first line looks for key hostPair in the map, and if it is not found, then it enters the if block where it inserts the key along with its value, and .first returns the iterator to the inserted item.
Improvement
But you can make an improvement to this. You could just write this:
iter = mymap.insert(make_pair(hostPair, Traffic())).first;
which is exactly equivalent to your code. No need to use find and then insert. The result is a performance gain.
If the key already exists, the insert function will NOT insert any item to the map and .first will return you the iterator to the found item. If the key doesn't exist, only then it will insert and .first will return you the iterator to the newly inserted item.
If you want to know whether the key already existed or not, then you can do this:
auto pair = mymap.insert(make_pair(hostPair, Traffic())); //NO .first!
if (pair.second)
{
//a newly created item is inserted
auto iter = pair.first; //iterator to the newly inserted item
}
else
{
//an item with key `hostPair` already exists in the map
auto iter = pair.first; //iterator to the found item
}
Hope that helps.
Related
So I have the following map parseTable
std::map<std::pair<Symbol, Symbol>, list<Symbol> > parseTable;
I am confused on how to access to the list value if I have my map initialized this way:
std::map<std::pair<Symbol, Symbol>, list<Symbol> > parseTable = {
{{Symbol::Input, Symbol::OpenPar}, {Symbol::Expr, Symbol::Semicolon, Symbol::InputP}},
{{Symbol::Input, Symbol::Ident}, {Symbol::Expr, Symbol::Semicolon, Symbol::InputP}},
{{Symbol::Input, Symbol::Number}, {Symbol::Expr, Symbol::Semicolon, Symbol::InputP}}
};
I want to access to each of the values of my list individually when I use the find() function of my map.
This is what I have come up with but I am not able to get a reference to that index value:
if (parseTable.find(std::pair(stack_symbol.top(), current_symbol)))
std::map::find will return an iterator to the found element, or to end if not found. That iterator will point to a std::pair<const Key, Value>, which in your case would translate to
std::pair< const std::pair<Symbol, Symbol>, list<Symbol> >
What you want is something like this
auto it = parseTable.find(std::pair(stack_symbol.top(), current_symbol));
if (it != parseTable.end()) { // A match was found
//it->first is std::pair<Symbol, Symbol>
//it->second is list<Symbol>
for (auto& symbol : it->second) {
//symbol is each individual value in the list
... do something with symbol
}
}
This is not the greatest choice for map key, it won't let the map to be used efficiently.
std::map::find() return an iterator to the place where it found searched item or std::map::end() if not found. So, in your if statement, you need to check against that:
std::map<std::pair<Symbol, Symbol>, list<Symbol> >::iterator iter =
parseTable.find(std::pair(stack_symbol.top(), current_symbol)) //or auto with C++11
if (iter != parseTable.end())
find returns an iterator, to access the object (which will be of type std::pair<std::pair<Symbol, Symbol>, list<Symbol>>, you'll need dereference operator *
Symbol currentSymbol = (*iter).first.second; //dummy example to show the use
std::list<Symbol> myList = (*iter).second'
Suppose I have an std::map and want to do something to the item with key X and every item with a higher key (remember, this is an ordered map). The code is obvious and clear:
auto iter = mymap.find(X);
while (iter != mymap.end()) {
process(*iter);
iter++;
}
or, possibly better, std::for_each(mymap.find(X), mymap.end(), process).
But if my task is to perform processing on the item with key X and every item with a lower key, in that order, I can't find a clean coding pattern that expresses intent.
auto iter mymap.find(x);
if (iter != mymap.end()) {
iter++; // Go forward...
while (iter != mymap.begin()) {
iter--; // ... so we can go backwards
process(*iter);
}
}
If I didn't want to do them in reverse order, it would be easy enough to increment the iterator returned by std::map::find() and then use std::for_each(mymap.begin(), incremented_iter, process).
Bidirectional iterators aren't reverse iterators, so I can't use mymap.rend() as the "off the beginning" to compare against in a while() loop.
Is there a clean way to do this in C++11 (or in +14 or +17, so I'll have something to look forward to)?
You want to process every item with key X or lower, and to do the processing starting at the element with key X and then go backwards. What you could do is use reverse_iterator on the first element past key X and create a reverse iterator, which points to the element just before the one you gave it. Then you can iterate until the "reverse end":
auto pastX = mymap.upper_bound(X); // First element with key > X
// make_reverse_iterator returns iter that dereferences to the previous element
for (auto iter = std::make_reverse_iterator(pastX); iter != mymap.rend(); ++iter)
// Use *iter
For better readability, I usually keep some helper functions to create an "iterable object" (one which can be called with .begin() and .end()) from a pair of iterators, so I would actually write:
auto pastX = mymap.upper_bound(X);
// "IteRange" would return a "range-for-iterable" object from two iterators
for (auto&& elem : IteRange(std::make_reverse_iterator(pastX), mymap.rend()))
// Use elem
I would like to get the item that goes before given key, for my std::multimap.
For the item that goes after given key I could simply use std::multimap::upper_bound (it will return the element with key greater then given). But unfortunately, std::multimap::lower_bound returns the element with the "lower or equal" key.
The sample taken from http://www.cplusplus.com/reference/map/multimap/lower_bound/:
mymultimap.insert(std::make_pair('a',10));
mymultimap.insert(std::make_pair('b',121));
mymultimap.insert(std::make_pair('c',1001));
mymultimap.insert(std::make_pair('c',2002));
mymultimap.insert(std::make_pair('d',11011));
mymultimap.insert(std::make_pair('e',44));
itlow = mymultimap.lower_bound ('b'); // itlow points to b
itup = mymultimap.upper_bound ('d'); // itup points to e (not d)
How to get the iterator (or the value) for a when you give b as a parameter?
You can use lower_bound, but there are two edge cases you have to consider: it returns begin() and it returns end():
auto itfound = mymultimap.lower_bound('b');
if (itfound == mymultimap.begin()) {
// 'b', or something past 'b', is the first item
// or the map is empty
// what to do here?
}
else if (itfound == mymultimap.end()) {
// there does not exist an item >= 'b'
// what to do here? possibly std::prev(end()) ?
}
else {
// ok cool, we found something in the middle
// just back up
--itfound;
// do stuff with itfound here
}
Go ahead and use lower_bound, then decrement the iterator you receive (after checking to make sure it isn't begin).
itfound = mymultimap.lower_bound('b');
if (itfound != mymultimap.begin())
{
--itfound;
// do something with itfound
}
You may need slightly different logic if the item isn't in the map, but that shouldn't be a hard modification.
We know the following that for any sequenced container (i.e. map, set, multimap and multiset):
lower_bound(key) returns the first entry >= key
upper_bound(key) returns the first entry > key
Each function also returns end() in the event there is no entry >= / > key.
So we have functionality based on the operators > and >=. We might reasonably ask, can we have functionality based on < and <= as well?
It is somewhat trivial to ask for the first entry < or <= key (this is either begin() or not in the container). However it is meaningful to ask for is the last such entry. Indeed, what you are asking for is the last entry < key.
For completeness we will consider the last entry <= key too.
Let's suppose we have such functions, what should they do if there is no such entry? We could do a lot worse than return end() too - it is a testable value and distinct from any successful case. It is also consistent with both lower_bound, upper_bound, as well as find and a whole host of standalone search functions in the <algorithm> header. The bottom line is that end() is routinely used to mean `no such entry' so this should be good enough for us as well.
As noted by Barry and Mark, calling lower_bound(key) and then decrementing will give us what we want, unless lower_bound(key) == begin(). In this case, the first (and thus [joint-]smallest) entry is >= key, so there are no entries < key - precisely the scenario where we just said we should return end().
For the case of looking for the last entry <= key we can do the same but with upper_bound, again returning end() to mean 'no such entry' when upper_bound(key) == begin().
Lets call these functions lower_bound_dec and upper_bound_dec respectively:
// Find the last entry < key, returning end() if all entries are >= key
// Container can be any that implements lower_bound, e.g. std::map, std::multimap, std::set or std::multiset
template<typename Container, typename Key>
typename Container::const_iterator lower_bound_dec(const Container &container, const Key &key) {
auto it = container.lower_bound(key);
if (it == std::begin(container))
it = std::end(container);
else
--it;
return it;
}
// Find the last entry <= key, returning end() if all entries are > key
// Container can be any that implements upper_bound, e.g. std::map, std::multimap, std::set or std::multiset
template<typename Container, typename Key>
typename Container::const_iterator upper_bound_dec(const Container &container, const Key &key) {
auto it = container.upper_bound(key);
if (it == std::begin(container))
it = std::end(container);
else
--it;
return it;
}
We might also define iterator based functions, that mirror std::lower_bound and std::upper_bound and can operate on any sorted range with bidirectional iterators:
// Find the last entry < key, returning end() if all entries are >= key
// last must be reachable from first, and all entries in sorted order between them
template<typename Iterator, typename Key>
Iterator lower_bound_dec(Iterator first, Iterator last, const Key &key) {
auto it = container.lower_bound(first, last, key);
if (it == first)
it = last;
else
--it; // requires bidirectional iterators
return it;
}
upper_bound(first, last, key) and the forms that take a comparator left as exercises to the reader / anyone who wants to earn rep by editing! :)
TL/DR:
Use lower_bound and then decrement, with the caveat that "decrementing" begin() yields end() to mean 'no such entry.'
#include <map>
...
multimap<char,int> mymap;
mymap.insert(pair<char,int>('a',10));
mymap.insert(pair<char,int>('b',15));
mymap.insert(pair<char,int>('b',20));
mymap.insert(pair<char,int>('c',25));
Say I now want to remove one of the pairs I have just added to the map.
I have examples to remove an entire key entry, which for key 'b' would remove both 'b',15 and 'b',20.
But what is the code to remove just, say, the pair 'b',20?
You can use std::multimap<char, int>::equal_range, which will give you an iterator range containing all pairs which have a certain key. So if you look for 'b', you will get an iterator range containing all pairs which have 'b' as the key.
You can then simply iterate over the range, and erase any pair you see fit, by erasing the iterator.
multimap<char,int> mymap;
mymap.insert(pair<char,int>('a',10));
mymap.insert(pair<char,int>('b',15));
mymap.insert(pair<char,int>('b',20));
mymap.insert(pair<char,int>('c',25));
typedef multimap<char, int>::iterator iterator;
std::pair<iterator, iterator> iterpair = mymap.equal_range('b');
// Erase (b,15) pair
//
iterator it = iterpair.first;
for (; it != iterpair.second; ++it) {
if (it->second == 15) {
mymap.erase(it);
break;
}
}
In case you need to continue iterating after the first match you need to first retrieve an iterator to the next element since the erased iterator gets invalidated.
One way to achieve this, starting from C++11, is to use the return value of the erase function which is an iterator to the element that follows the last element removed (or multimap::end, if the last element was removed). Beware the key based version returns the number of elements erased, not an iterator.
Building on top of the valuable Charles Salvia answer, showing how to erase (b,15 ) pair, you get
multimap<char,int> mymap;
mymap.insert(pair<char,int>('a',10));
mymap.insert(pair<char,int>('b',15));
mymap.insert(pair<char,int>('b',20));
mymap.insert(pair<char,int>('c',25));
typedef multimap<char, int>::iterator iterator;
std::pair<iterator, iterator> iterpair = mymap.equal_range('b');
// Erase (b,15) pair
//
iterator it = iterpair.first;
for (; it != iterpair.second; ) {
if (it->second == 15) {
it=mymap.erase(it);
}
else
++it;
}
I was trying to erase a range of elements from map based on particular condition. How do I do it using STL algorithms?
Initially I thought of using remove_if but it is not possible as remove_if does not work for associative container.
Is there any "remove_if" equivalent algorithm which works for map ?
As a simple option, I thought of looping through the map and erase. But is looping through the map and erasing a safe option?(as iterators get invalid after erase)
I used following example:
bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}
int main(void)
{
std::map<int, std::string> aMap;
aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";
// does not work, an error
// std::remove_if(aMap.begin(), aMap.end(), predicate);
std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
return 0;
}
Almost.
for(; iter != endIter; ) {
if (Some Condition) {
iter = aMap.erase(iter);
} else {
++iter;
}
}
What you had originally would increment the iterator twice if you did erase an element from it; you could potentially skip over elements that needed to be erased.
This is a common algorithm I've seen used and documented in many places.
[EDIT] You are correct that iterators are invalidated after an erase, but only iterators referencing the element that is erased, other iterators are still valid. Hence using iter++ in the erase() call.
erase_if for std::map (and other containers)
I use the following template for this very thing.
namespace stuff {
template< typename ContainerT, typename PredicateT >
void erase_if( ContainerT& items, const PredicateT& predicate ) {
for( auto it = items.begin(); it != items.end(); ) {
if( predicate(*it) ) it = items.erase(it);
else ++it;
}
}
}
This won't return anything, but it will remove the items from the std::map.
Usage example:
// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
return /* insert appropriate test */;
});
Second example (allows you to pass in a test value):
// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4; // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
return item.property < test_value; // or whatever appropriate test
});
Now, std::experimental::erase_if is available in header <experimental/map>.
See: http://en.cppreference.com/w/cpp/experimental/map/erase_if
Here is some elegant solution.
for (auto it = map.begin(); it != map.end();)
{
(SomeCondition) ? map.erase(it++) : (++it);
}
For those on C++20 there are built-in std::erase_if functions for map and unordered_map:
std::unordered_map<int, char> data {{1, 'a'},{2, 'b'},{3, 'c'},{4, 'd'},
{5, 'e'},{4, 'f'},{5, 'g'},{5, 'g'}};
const auto count = std::erase_if(data, [](const auto& item) {
auto const& [key, value] = item;
return (key & 1) == 1;
});
I got this documentation from the excellent SGI STL reference:
Map has the important property that
inserting a new element into a map
does not invalidate iterators that
point to existing elements. Erasing an
element from a map also does not
invalidate any iterators, except, of
course, for iterators that actually
point to the element that is being
erased.
So, the iterator you have which is pointing at the element to be erased will of course be invalidated. Do something like this:
if (some condition)
{
iterator here=iter++;
aMap.erase(here)
}
The original code has only one issue:
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
Here the iter is incremented once in the for loop and another time in erase, which will probably end up in some infinite loop.
From the bottom notes of:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
a Pair Associative Container cannot provide mutable iterators (as defined in the Trivial Iterator requirements), because the value type of a mutable iterator must be Assignable, and pair is not Assignable. However, a Pair Associative Container can provide iterators that are not completely constant: iterators such that the expression (*i).second = d is valid.
First
Map has the important property that inserting a new element into a map does not invalidate iterators that point to existing elements. Erasing an element from a map also does not invalidate any iterators, except, of course, for iterators that actually point to the element that is being erased.
Second, the following code is good
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
When calling a function, the parameters are evaluated before the call to that function.
So when iter++ is evaluated before the call to erase, the ++ operator of the iterator will return the current item and will point to the next item after the call.
IMHO there is no remove_if() equivalent.
You can't reorder a map.
So remove_if() can not put your pairs of interest at the end on which you can call erase().
Based on Iron Savior's answer For those that would like to provide a range more along the lines of std functional taking iterators.
template< typename ContainerT, class FwdIt, class Pr >
void erase_if(ContainerT& items, FwdIt it, FwdIt Last, Pr Pred) {
for (; it != Last; ) {
if (Pred(*it)) it = items.erase(it);
else ++it;
}
}
Curious if there is some way to lose the ContainerT items and get that from the iterator.
Steve Folly's answer I feel the more efficient.
Here is another easy-but-less efficient solution:
The solution uses remove_copy_if to copy the values we want into a new container, then swaps the contents of the original container with those of the new one:
std::map<int, std::string> aMap;
...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;
//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(),
inserter(aTempMap, aTempMap.end()),
predicate);
//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);
If you want to erase all elements with key greater than 2, then the best way is
map.erase(map.upper_bound(2), map.end());
Works only for ranges though, not for any predicate.
I use like this
std::map<int, std::string> users;
for(auto it = users.begin(); it <= users.end()) {
if(<condition>){
it = users.erase(it);
} else {
++it;
}
}