I'm trying to increment key of nodes after the index j in a map, but I got some error while incrementing the iterator to the next node, here's my code :
typedef map<int, string> My_map;
My_map my_map;
my_map[0] = "la base";
my_map[1] = "le premier";
int j = 1;
My_map::reverse_iterator first_it(my_map.rbegin());
first_it++;
My_map::reverse_iterator last_it(make_reverse_iterator(next(my_map.begin(), j - 1)));
for (My_map::reverse_iterator it = first_it ; it != last_it ; it++) {
auto handle = my_map.extract(it.base());
handle.key()++;
my_map.insert(move(handle));
}
I know that a map cannot have duplicate keys, so I started incrementing from the last one to the j-th one. But the it++ does not work. Is it different from when I used first_it++; ?
In the documentation for std::map::extract it mentions the side-effects:
Extracting a node invalidates only the iterators to the extracted element. Pointers and references to the extracted element remain valid, but cannot be used while element is owned by a node handle: they become usable if the element is inserted into a container.
As you can see, all the iterators are find except for the ones you care about. Because it is an iterator to the extracted element, it is now invalid. Subsequent attempts to use it (with it++ to advance the loop iteration) leads to undefined behavior.
What you can do is use the iterator returned by std::map::insert:
auto result = my_map.insert(move(handle));
it = make_reverse_iterator(result.position);
As pointed by #paddy, after calling .extract() method and doing .insert() again all your iterators are invalidated hence you can't run modifying loop any further.
I suggest following solution which is valid and faster. Into separate std::vector copy all elements starting from j-th position. Delete them in map. Modify them in vector the way you like, for example by adding +1 to all keys. Insert them all back at once by .insert(begin, end) method of a map. Clear vector of copied elements.
All suggested is implemented in below code snippet:
Try it online!
#include <map>
#include <string>
#include <vector>
#include <iostream>
int main() {
std::map<int, std::string> m;
m[0] = "la base";
m[1] = "le premier";
int j = 1;
auto const j_it = std::next(m.begin(), j);
std::vector<std::pair<int, std::string>> vcopy(j_it, m.end());
m.erase(j_it, m.end());
for (auto & [k, v]: vcopy)
++k;
m.insert(vcopy.begin(), vcopy.end());
vcopy.clear();
// Show map
for (auto const & [k, v]: m)
std::cout << k << ": " << v << std::endl;
}
Output:
0: la base
2: le premier
Related
here i is a pointer so that I can increment and decrement too, but it's not working if I do minus operations as I mentioned in the code
for(auto i = soldier.begin();i!=soldier.end();i++){
int temp1 = i->first;
int temp2 = i->second;
i->first = (i-temp2)->first;
i->second = (i-temp2)->second;
(i-temp2)->first = temp1;
(i-temp2)->second = temp2;
}
Just swap the element, you only need operator[], but you should be sure there is the element:
map<int, int> m;
m.insert(std::pair<int, int>(1,100));
m.insert(std::pair<int, int>(2,1000));
// change
int temp = m[1];
m[1] = m[2];
m[2] = temp;
here i is a pointer so that I can increment and decrement too, but it's not working if I do minus operations
i in your code is an iterator, not a pointer. You cannot decrement a forward iterator.
Following your objective as per question title, you can only swap the values pertaining to keys in your map soldier by creating iterators to iterate through the map in search of two keys using std::map::find() and then swap using std::swap():
std::map<DT1, DT2> soldier;
// insert elements (key,value)
// create iterators over the associative map container with your required data types for <key,value> :
const std::map<DT1, DT2>::iterator i1 = soldier.find(key1);
const std::map<DT1,DT2>::iterator i2 = soldier.find(key2);
// you can use auto as well:
// auto i1 = soldier.find(key1);
// auto i2 = soldier.find(key2);
if ((i1 != soldier.end()) && (i2 != soldier.end()))
{ std::swap(i1->second, i2->second);
}
This will swap the values of the corresponding keys in between key1 and key2 in your map. You haven't mentioned the data types (nor is it shown in code) used for the key-value pairs in your map so am providing a generalized answer with data types DT1 and DT2.
Note that:
Using std::map::find searches for a key (find(const key& key)) and can't be used with values.
You cannot use an iterator to swap two keys. Also its pointless to swap both keys and values (as mentioned by churill as well) as that would mean your just changing the order of <key,value> pairs.
An example with a map of <int ,std::string> key-value pairs:
#include <iostream>
#include <iterator>
#include <map>
int main()
{
std::map<int,std::string> soldier;
soldier[1]="Soldier1";
soldier[2]="Soldier2";
const std::map<int ,std::string>::iterator i1 = soldier.find(1);
const std::map<int ,std::string>::iterator i2 = soldier.find(2);
if ((i1 != soldier.end()) && (i2 != soldier.end()))
{ std::swap(i1->second, i2->second);
}
std::map<int,std::string>::iterator it;
for(it=soldier.begin(); it!=soldier.end(); ++it)
std::cout<<it->first<<" "<<it->second<<"\n";
// 1 Soldier2
// 2 Soldier1
return 0;
}
How do I remove from a map while iterating it? like:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
If I use map.erase it will invalidate the iterators
The standard associative-container erase idiom:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Note that we really want an ordinary for loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.
Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p where p is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.
I personally prefer this pattern which is slightly clearer and simpler, at the expense of an extra variable:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Advantages of this approach:
the for loop incrementor makes sense as an incrementor;
the erase operation is a simple erase, rather than being mixed in with increment logic;
after the first line of the loop body, the meaning of it and next_it remain fixed throughout the iteration, allowing you to easily add additional statements referring to them without headscratching over whether they will work as intended (except of course that you cannot use it after erasing it).
Assuming C++11, here is a one-liner loop body, if this is consistent with your programming style:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
A couple of other minor style changes:
Show declared type (Map::const_iterator) when possible/convenient, over using auto.
Use using for template types, to make ancillary types (Map::const_iterator) easier to read/maintain.
The C++20 draft contains the convenience function std::erase_if.
So you can use that function to do it as a one-liner.
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});
In short "How do I remove from a map while iterating it?"
With old map impl: You can't
With new map impl: almost as #KerrekSB suggested. But there are some syntax issues in what he posted.
From GCC map impl (note GXX_EXPERIMENTAL_CXX0X):
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
* #return An iterator pointing to the element immediately following
* #a position prior to the element being erased. If no such
* element exists, end() is returned.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
iterator
erase(iterator __position)
{ return _M_t.erase(__position); }
#else
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
void
erase(iterator __position)
{ _M_t.erase(__position); }
#endif
Example with old and new style:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> t_myVec;
int main() {
cout << "main() ENTRY" << endl;
t_myMap mi;
mi.insert(t_myMap::value_type(1,1));
mi.insert(t_myMap::value_type(2,1));
mi.insert(t_myMap::value_type(3,1));
mi.insert(t_myMap::value_type(4,1));
mi.insert(t_myMap::value_type(5,1));
mi.insert(t_myMap::value_type(6,1));
cout << "Init" << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
t_myVec markedForDeath;
for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
if (it->first > 2 && it->first < 5)
markedForDeath.push_back(it->first);
for(size_t i = 0; i < markedForDeath.size(); i++)
// old erase, returns void...
mi.erase(markedForDeath[i]);
cout << "after old style erase of 3 & 4.." << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
for (auto it = mi.begin(); it != mi.end(); ) {
if (it->first == 5)
// new erase() that returns iter..
it = mi.erase(it);
else
++it;
}
cout << "after new style erase of 5" << endl;
// new cend/cbegin and lambda..
for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});
return 0;
}
prints:
main() ENTRY
Init
1-1
2-1
3-1
4-1
5-1
6-1
after old style erase of 3 & 4..
1-1
2-1
5-1
6-1
after new style erase of 5
1-1
2-1
6-1
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
Pretty sad, eh? The way I usually do it is build up a container of iterators instead of deleting during traversal. Then loop through the container and use map.erase()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}
How do I remove from a map while iterating it? like:
std::map<K, V> map;
for(auto i : map)
if(needs_removing(i))
// remove it from the map
If I use map.erase it will invalidate the iterators
The standard associative-container erase idiom:
for (auto it = m.cbegin(); it != m.cend() /* not hoisted */; /* no increment */)
{
if (must_delete)
{
m.erase(it++); // or "it = m.erase(it)" since C++11
}
else
{
++it;
}
}
Note that we really want an ordinary for loop here, since we are modifying the container itself. The range-based loop should be strictly reserved for situations where we only care about the elements. The syntax for the RBFL makes this clear by not even exposing the container inside the loop body.
Edit. Pre-C++11, you could not erase const-iterators. There you would have to say:
for (std::map<K,V>::iterator it = m.begin(); it != m.end(); ) { /* ... */ }
Erasing an element from a container is not at odds with constness of the element. By analogy, it has always been perfectly legitimate to delete p where p is a pointer-to-constant. Constness does not constrain lifetime; const values in C++ can still stop existing.
I personally prefer this pattern which is slightly clearer and simpler, at the expense of an extra variable:
for (auto it = m.cbegin(), next_it = it; it != m.cend(); it = next_it)
{
++next_it;
if (must_delete)
{
m.erase(it);
}
}
Advantages of this approach:
the for loop incrementor makes sense as an incrementor;
the erase operation is a simple erase, rather than being mixed in with increment logic;
after the first line of the loop body, the meaning of it and next_it remain fixed throughout the iteration, allowing you to easily add additional statements referring to them without headscratching over whether they will work as intended (except of course that you cannot use it after erasing it).
Assuming C++11, here is a one-liner loop body, if this is consistent with your programming style:
using Map = std::map<K,V>;
Map map;
// Erase members that satisfy needs_removing(itr)
for (Map::const_iterator itr = map.cbegin() ; itr != map.cend() ; )
itr = needs_removing(itr) ? map.erase(itr) : std::next(itr);
A couple of other minor style changes:
Show declared type (Map::const_iterator) when possible/convenient, over using auto.
Use using for template types, to make ancillary types (Map::const_iterator) easier to read/maintain.
The C++20 draft contains the convenience function std::erase_if.
So you can use that function to do it as a one-liner.
std::map<K, V> map_obj;
//calls needs_removing for each element and erases it, if true was reuturned
std::erase_if(map_obj,needs_removing);
//if you need to pass only part of the key/value pair
std::erase_if(map_obj,[](auto& kv){return needs_removing(kv.first);});
In short "How do I remove from a map while iterating it?"
With old map impl: You can't
With new map impl: almost as #KerrekSB suggested. But there are some syntax issues in what he posted.
From GCC map impl (note GXX_EXPERIMENTAL_CXX0X):
#ifdef __GXX_EXPERIMENTAL_CXX0X__
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
* #return An iterator pointing to the element immediately following
* #a position prior to the element being erased. If no such
* element exists, end() is returned.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
iterator
erase(iterator __position)
{ return _M_t.erase(__position); }
#else
/**
* #brief Erases an element from a %map.
* #param position An iterator pointing to the element to be erased.
*
* This function erases an element, pointed to by the given
* iterator, from a %map. Note that this function only erases
* the element, and that if the element is itself a pointer,
* the pointed-to memory is not touched in any way. Managing
* the pointer is the user's responsibility.
*/
void
erase(iterator __position)
{ _M_t.erase(__position); }
#endif
Example with old and new style:
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
typedef map<int, int> t_myMap;
typedef vector<t_myMap::key_type> t_myVec;
int main() {
cout << "main() ENTRY" << endl;
t_myMap mi;
mi.insert(t_myMap::value_type(1,1));
mi.insert(t_myMap::value_type(2,1));
mi.insert(t_myMap::value_type(3,1));
mi.insert(t_myMap::value_type(4,1));
mi.insert(t_myMap::value_type(5,1));
mi.insert(t_myMap::value_type(6,1));
cout << "Init" << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
t_myVec markedForDeath;
for (t_myMap::const_iterator it = mi.begin(); it != mi.end() ; it++)
if (it->first > 2 && it->first < 5)
markedForDeath.push_back(it->first);
for(size_t i = 0; i < markedForDeath.size(); i++)
// old erase, returns void...
mi.erase(markedForDeath[i]);
cout << "after old style erase of 3 & 4.." << endl;
for(t_myMap::const_iterator i = mi.begin(); i != mi.end(); i++)
cout << '\t' << i->first << '-' << i->second << endl;
for (auto it = mi.begin(); it != mi.end(); ) {
if (it->first == 5)
// new erase() that returns iter..
it = mi.erase(it);
else
++it;
}
cout << "after new style erase of 5" << endl;
// new cend/cbegin and lambda..
for_each(mi.cbegin(), mi.cend(), [](t_myMap::const_reference it){cout << '\t' << it.first << '-' << it.second << endl;});
return 0;
}
prints:
main() ENTRY
Init
1-1
2-1
3-1
4-1
5-1
6-1
after old style erase of 3 & 4..
1-1
2-1
5-1
6-1
after new style erase of 5
1-1
2-1
6-1
Process returned 0 (0x0) execution time : 0.021 s
Press any key to continue.
Pretty sad, eh? The way I usually do it is build up a container of iterators instead of deleting during traversal. Then loop through the container and use map.erase()
std::map<K,V> map;
std::list< std::map<K,V>::iterator > iteratorList;
for(auto i : map ){
if ( needs_removing(i)){
iteratorList.push_back(i);
}
}
for(auto i : iteratorList){
map.erase(*i)
}
I want to reverse the list a={1,2,3,4,5} using an iterator.According to my knowledge i know an iterator is a pointer that points to an element in the list.
So there are two questions which are as follows:
Why does this code fail even though I am pointing to the last element in the list and then decrementing the pointer to the previous address?
list<int>::iterator i;
for(i=l.end();i!=l.begin();i--)
cout<<(*i);
Second question is how do you access the third element in the list using an iterator without a for loop.Is it possible to access it without using a for loop?Please state the syntax if it is possible.
l.end() points after the last element, so in principle you need:
for(i=l.end()-1; i!=l.begin(); i--) // invalid
but then you'll not get the first element (l.begin()) as you stop when you reach it.
Further it is not valid to subtract 1 from a list iterator so it must be:
for(i=std::prev(l.end()); i!=l.begin(); i--) // valid
But still you have the problem with the first element.
A better way is to use reverse iterator. Something like:
for (std::list<int>::reverse_iterator i = l.rbegin(); i != l.rend(); ++i)
For your second question you could do:
auto it = l.begin();
cout << *(std::next(it, 2));
Here is a list of valid operations depending on iterator type: http://www.cplusplus.com/reference/iterator/
And here you can see that list has a bidirectional iterator: http://www.cplusplus.com/reference/list/list/
You can use reverse_iterator to travel a container in reverse.
list<int>::reverse_iterator i;
for(i=l.rbegin();i!=l.rend();i++)
cout<<(*i);
As for acessing the 3rd element of the list you have to iterate to it. There is no direct access to elements in a list
You can do it also By list:
for (std::list<int>::reverse_iterator rit=mylist.rbegin(); rit!=mylist.rend(); ++rit)
std::cout << ' ' << *rit;
You can do it By Vector in C++.
int temp[] = {1,2,3,4,5};
std::vector<int>v(temp, temp+3);
std::vector<int> n_v(v.rbegin(), v.rend());
Also, if you want to reverse a list in place, you can use this std::reverse.
#include <iostream>
#include <list>
#include <algorithm>
int main()
{
std::list<int> values { 1, 2, 3, 4, 5 };
std::reverse(values.begin(), values.end());
for (int i : values)
std::cout << i << " ";
}
map<int, string>::reverse_iterator& it = temp.rbegin();
it -> points to garbage key value
it++ -> points to the correct key value
map<int, string>::iterator& it = temp.begin();
it-> points to the correct key value from beginning.
Please assist.
Your statements are incorrect. If temp is not empty, then *temp.rbegin() is indeed the last value in the map, and *temp.begin() is the first value.
(However, the underlying iterator of the reverse begin is the ordinary end iterator - but you don't see that unless you call base() on the reverse iterator.)
You must have an error in your code that's filling the map. You can verify this by testing a trivial example such as
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<int, char> coll;
// insert elements from 1 to 9
for (int i=1; i<=9; ++i) {
coll[i] = static_cast<char>(i+'a'-1); // because adding 96 is less obvious that we're indexing based at letter a
}
// print all element in reverse order
for_each (coll.rbegin(), coll.rend(),
[]( pair<int, char> mapinfo ) { cout << mapinfo.second << " "; } );
cout << endl;
}