How to update current iterator while iterating

So in this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
			std::map<std::string, int> map;

			map["a"] = 1;

			int times = 0;

			for (auto i = map.begin(), e = map.end(); i != e;) {

				auto current = i++;

				times++;

				if (current->first == "a") map.insert(std::pair<std::string, int>("c", 3));

			}

			std::cout << times << std::endl; //prints 1. 


The for loop loops only once but it has to loop twice, because I added one more pair to map. How do I update iterator every loop if an pair is added or removed?
Thanks :).
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <map>
#include <string>

int main()
{
    std::map<std::string, int> myMap{};
    myMap[std::string("a")] = 1;
    bool match = false;
    int times{};

    for (auto itr = myMap.begin(); itr != myMap.end(); ++itr)
   {
        if(((*itr).first == "a") && (match == false))//bool prevents endless loop
       {
           myMap[std::string("c")] = 3;
           match = true;
           itr = myMap.begin();//take the iterator back to the beginning
        }
       ++times;
    }
    std::cout << times;
}


> The for loop loops only once but it has to loop twice, because I added one more pair to map.

It will iterate over the newly added item only if that item appears in the ordered sequence after the current item that is being iterated over. (This is guaranteed only if we haven't prematurely advanced the iterator before the insertion was done.)

If that item appears before the current item that is being iterated over, it will not be encountered as the iteration progresses forward from the current item.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string>
#include <map>
#include <iomanip>

int main()
{
    {
        std::map<std::string, int> map;
        map["5"] = 5;
        int iteration_count = 0;

        for( auto iter = map.begin(); iter != map.end(); ++iter )
        {
            std::cout << '{' << std::quoted(iter->first) << ',' << iter->second << "} " ;
            iteration_count++;

            if( iter->first == "5" )
            {
                map.emplace( "8", 8 ); // in the ordered sequence, key "8" appears after "5"
                                       // key "8" would be encountered later in the iteration

                map.emplace( "3", 3 ); // in the ordered sequence, key "3" appears before "5"
                                       // key "3" would not be encountered later in the iteration
            }
        }

        std::cout << "\niteration_count: " << iteration_count << '\n' ; // prints 2.
    }

    std::cout << "\n----------------------------------\n\n" ;

    {
        std::map<std::string, int> map;
        map["5"] = 5;
        int iteration_count = 0;

        for( auto& pair : map )
        {
            std::cout << '{' << std::quoted(pair.first) << ',' << pair.second << "} " ;
            iteration_count++;

            if( pair.first == "5" )
            {
                map.emplace( "8", 8 ); // in the ordered sequence, key "8" appears after "5"
                                       // key "8" would be encountered later in the iteration

                map.emplace( "3", 3 ); // in the ordered sequence, key "3" appears before "5"
                                       // key "3" would not be encountered later in the iteration
            }
        }

        std::cout << "\niteration_count: " << iteration_count << '\n' ; //prints 2.
    }
}

http://coliru.stacked-crooked.com/a/bc05feb627300c39
Last edited on
But is there something like this?
1
2
3
4
5
6
7
8
9
			for (auto i = map.begin(); i != map.end(); i++) {

				map.insert(std::pair<std::string, int>("something", 5));
				//i->something (maybe remove or add new pair).
				//only future pairs will be added or removed.

				i = map.updateIterator(); //to update iterator

			}
> i = map.updateIterator(); //to update iterator
¿update how? ¿what do you want to happen/not happen?
¿what's the problem with JLBorges' code?
If the intent is that the inserted items should also be processed, then:

if the inserted item would appear before the current item in the sorted sequence, we would need to process it separately if it is not to be missed (perhaps process it immediately after its insertion).

if the inserted item would appear after the current item in the sorted sequence, we do not need to do anything special; it would be encountered normally as the iteration progresses.

Something along these lines:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <string>
#include <map>
#include <iomanip>

int main()
{
    std::map<std::string, int> map { { "4", 4 }, { "5", 5 },  { "6", 6 }, { "8", 8 }  } ;

    // insert these two items when we encounter key "5"
    const decltype(map)::value_type items_to_be_inserted[] = { { "3", 3 }, { "7", 7 } } ;
    const decltype(map)::key_type insert_when = "5" ;

    int items_processed = 0 ;
    const auto process_item = [ &items_processed ] ( const auto& kvpair )
    {
        std::cout << '{' << std::quoted(kvpair.first) << ',' << kvpair.second << "} " ;
        ++items_processed ;
    };

    for( auto& pair : map )
    {
        process_item(pair) ;

        if( pair.first == insert_when )
        {
            for( auto& kvi : items_to_be_inserted )
            {
                // if the item was actually inserted
                // and it would appear before the current item in the sorted sequence
                if( map.insert(kvi).second && map.key_comp()( kvi.first, insert_when ) )
                    process_item(kvi) ; // process it so that it is not omitted

                // else item would appear later and we would encounter it if it was inserted
            }
        }
    }

    std::cout << "\nitems_processed: " << items_processed << '\n' ; //prints 6.
}

http://coliru.stacked-crooked.com/a/7499734db9819300
¿update how? ¿what do you want to happen/not happen?

To just initialize new pairs from map to iterator. For future elements that are going to listed. For example (my real code):
1
2
3
4
5
6
7
8
9
10
		for (auto i = listeners_[event->type].begin(), e = listeners_[event->type].end(); i != e;) {

			auto current = i++;

			current->second(event); //Calls the listener function. Adds new elements to buffer container

			updateEvents(); //adds elements from buffer container to container.
			//Problem is here. It adds new pairs to container but iterator doesn't list them.

		}
One thing that you need to be very very clear on: it's often invalid to update a container while you're iterating over it. You're lucky here because map<> is one of the most forgiving of the containers. The lesson is to be sure you understand how an iterator behaves when you modify the container.

If you need to Do Something with the newly inserted items, then you might need to make two passes:
1
2
3
4
5
6
7
8
9
for( auto iter = map.begin(); iter != map.end(); ++iter ) {
    if (some condition) {
            insert new item in a DIFFERENT map
    }
}
for( auto iter = differentMap.begin(); iter != differentMapap.end(); ++iter ) {
    map[iter->first] = iter->second;
    Do Something
}

This way you can get around the fact that the new item might appear before the current iterator.
Topic archived. No new replies allowed.