crandrades
crandrades

Reputation: 715

Iterating over a vector of maps, and inserting a copy if a condition is satisfied

I'm new to stl in c++ and I want to iterate over a vector of maps. Then, if one of these maps satisfies a certain condition, I want to make a copy of this map an then insert the copy into the vector.

For example:

int main(){
   vector<map<string, int> > my_vector;
   map<string, int> my_map;
   my_map["zero"] = 0;
   my_vector.push_back(my_map);

   for(vector<map<string, int> >::iterator iter1 = my_vector.begin();
                                           iter1 != my_vector,end();
                                           iter1++){
      for(map<string, int>::iterator iter2 = (*iter1).begin();
                                     iter2 != (*iter1).end();
                                     iter2++){
         if(iter2->second == 0){
            // make a copy of the map (*iter1), make some changes on it,
            // then insert the copy in the vector
         }
      }
   }

   return 0;
}

I have tried with:

map<string, int> new_map = *iter1;
new_map["zero"]++;  // to avoid an infinite loop
my_vector.push_back(new_map);

But the program crashes. No compiler error, only a program crash. Then I found this question and answers in stackoverflow, so I tried another way like this:

int main(){
   vector<map<string, int> > my_vector;
   map<string, int> my_map;
   my_map["zero"] = 0;
   my_vector.push_back(my_map);

   int remaining = my_vector.size();
   int current_position = 0;

   while(remaining>0){
      for(map<string, int>:: iterator iter1 = my_vector[index].begin();
                                      iter1 != my_vector[index].end();
                                      iter1++){
         if(iter1->second == 0){
            map<string, int> new_map = my_vector[0];
            new_map["zero"]++;  // to avoid an infinite loop
            my_vector.push_back(new_map);
         }
      }

   index++;
   remaining = my_vector.size()-index;
   }

   return 0;
}

But the program is still crashing, so I think the problem (or one of the problems) would be not only the iterator, but also the "copy" of the map.

If anyone have an idea of how I should do this, I will really appreciate it.

Upvotes: 0

Views: 1944

Answers (4)

efux
efux

Reputation: 466

This is my solution. It uses C++11 (because it looks so much better). Especially with collections C++11 gives the workflow a real boost.

 #include <vector>
 #include <string>
 #include <map>
 #include <iostream>
 using namespace std ;

 void merge(vector<map<string,int>>& from, vector<map<string,int>>& to) 
 {   
      for(auto entry : from) {
         to.push_back(entry) ;
      }   
 }   

 void search(vector<map<string,int>>& vec) 
 {   
      int iCount = 0 ; 
      vector<map<string, int>> cVector ;
      for(auto vmap : vec) {
         for(auto mapentry : vmap) {
              if(mapentry.second == 0) {
                   iCount++ ;
                   map<string, int> new_map = vmap ;
                   cVector.push_back(new_map) ;
              }   
         }   
      }   

      cout << "iCount: " << iCount << endl ;

      merge(cVector, vec) ;
 }   

 int main(){
      vector<map<string, int> > my_vector;
      map<string, int> my_map;
      my_map["zero"] = 0;
      my_map["third"] = 2 ; 
      my_map["second"] = 0 ; 
      my_vector.push_back(my_map);

      cout << my_vector.size() << endl ;

      vector<map<string,int>> cVector ;

      search(my_vector) ;

      cout << my_vector.size() << endl ;

      return 0;
 }

As written by Alex Antonov the push_back invalidates your Iterator because the memory gets reallocated. My answer creates a new vector and copies the entries after the search back in the original vector (my_vector).

About range base for-loops see this.

Upvotes: 0

Alex
Alex

Reputation: 942

Your program may crash because push_back can invalidate iterators. For example if a call to push_back leads to memory reallocation (it happens when you exceed current capacity of the vector) then all vector's iterators become invalid (they point to deallocated memory).
To solve that problem you can use indexes instead of iterators to access the vector's elements or you can push_back new elements to another copy of the vector.

In other words: don't use an old vector::iterator after you push_back'ed a new element into the vector.

Upvotes: 2

user1508519
user1508519

Reputation:

I probably have the wrong idea, but I think you can do something like this. You iterate over the elements of the vector, use the predicate to see if a current element fits the criteria, then use back_inserter.

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(){
   vector<map<string, int> > my_vector;
   map<string, int> my_map;
   my_map["zero"] = 0;
   my_vector.push_back(my_map);

   auto predicate = [](const map<string, int>& cur) {
    for (const auto& pair : cur)
    {
        if (pair.second == 0)
            return true;
    }

    return false;
   };

   std::copy_if(my_vector.begin(), my_vector.end(), back_inserter(my_vector), predicate);

   return 0;
}

Upvotes: 0

Abhishek Bansal
Abhishek Bansal

Reputation: 12715

One way you can do is:

size_t orig_size = my_vector.size();
for( size_t i = 0; i < orig_size; i++ ) {
  //...
}

Other way:
Build a new vector and then afterwards append the contents of this new vector to the original vector.

Upvotes: 0

Related Questions