How to copy_f from map to vector?

I would like to copy the values ​​that match the predicate (equal to ints) from map<string,int> to vector<int> .

This is what I tried:

 #include <map> #include <vector> #include <algorithm> int main() { std::vector< int > v; std::map< std::string, int > m; m[ "1" ] = 1; m[ "2" ] = 2; m[ "3" ] = 3; m[ "4" ] = 4; m[ "5" ] = 5; std::copy_if( m.begin(), m.end(), v.begin(), [] ( const std::pair< std::string,int > &it ) { return ( 0 == ( it.second % 2 ) ); } ); } 

Error message from g ++ 4.6.1:

 error: cannot convert 'std::pair<const std::basic_string<char>, int>' to 'int' in assignment 

Is there a way to edit the example to execute the above copy?

+8
c ++ c ++ 11 copy
source share
6 answers

With boost::range it is as simple as:

 boost::push_back( v, m | boost::adaptors::map_values | boost::adaptors::filtered([](int val){ return 0 == (val % 2); })); 
+12
source share

Problem

Copy error due to the fact that you are copying from map::iterator , which iterates over pair<string const,int> , to vector::iterator , which iterates over int .

Decision

Replace copy_if with for_each and do push_back on your vector.

Example

 std::for_each( m.begin(), m.end(), [&v] ( std::pair< std::string const,int > const&it ) { if ( 0 == ( it.second % 2 ) ) { v.push_back(it.second); } } ); 
+11
source share

The compiler error is actually quite brief:

 error: cannot convert 'std::pair<const std::basic_string<char>, int>' to 'int' in assignment 

And that is exactly what the problem is. map you are copying has iterators that split into pair<KEY,VALUE> , and there is no way to implicitly convert pair<KEY,VALUE> to VALUE only.

Because of this, you cannot use copy or copy_if to copy from map to vector ; but the standard library provides an algorithm that you can use, creatively called transform . transform very similar to copy in that it accepts two source iterators and a destination iterator. The transform difference also accepts a unary function that performs the actual conversion. Using lambda C ++ 11, you can copy the entire contents of map to vector as follows:

 transform( m.begin(), m.end(), back_inserter(v), [] (const MyMap::value_type& vt) { return vt.second; }); 

What if you do not want to copy the entire contents of the map , but only some elements that meet the certian criteria? Simple, just use transform_if .

What is it, you say? Is there no transform_if in the standard library? Well yes, you have a point there. Disappointing that there is no transform_if in the standard library. However, writing one is a fairly simple task. Here is the code:

 template<class InputIterator, class OutputIterator, class UnaryFunction, class Predicate> OutputIterator transform_if(InputIterator first, InputIterator last, OutputIterator result, UnaryFunction f, Predicate pred) { for (; first != last; ++first) { if( pred(*first) ) *result++ = f(*first); } return result; } 

As you would expect, using transform_if like accepting copy_if and merging it with transform . Here are some psudo code to demonstrate:

 transform_if( m.begin(), m.end(), back_inserter(v), [] (const MyMap::value_type& vt) // The UnaryFunction takes a pair<K,V> and returns a V { return vt.second; }, [] (const MyMap::value_type& vt) // The predicate returns true if this item should be copied { return 0 == (vt.second%2); } ); 
+5
source share

I cannot understand why a simple solution for a loop is not the preferred approach for this problem

 for (std::map< std::string, int >::iterator it = m.begin(); it != m.end(); ++it ) { if ((it->second % 2) == 0) v.push_back(it->second); } 

Except it makes the code more readable, it works better. I wrote a simple test to see how the for loop works compared to other proposed solutions:

 #include <iostream> #include <map> #include <vector> #include <algorithm> #include <stdlib.h> #include <time.h> #include <sstream> int main(int argc, char *argv[]) { std::map< std::string, int > m; std::vector<int> v; // Fill the map with random values... srand ( time(NULL) ); for (unsigned i=0; i<10000; ++i) { int r = rand(); std::stringstream out; out << r; std::string s = out.str(); m[s] = r; } /////////// FOR EACH //////////////////// clock_t start1 = clock(); for (unsigned k=0; k<10000; k++) { v.clear(); std::for_each( m.begin(), m.end(), [&v] ( const std::pair< std::string,int > &it ) { if ( 0 == ( it.second % 2 ) ) { v.push_back(it.second); } } ); } clock_t end1=clock(); std::cout << "Execution Time for_each : " << (end1-start1) << std::endl; /////////// TRANSFORM //////////////////// clock_t start2 = clock(); for (unsigned k=0; k<10000; k++) { v.clear(); std::transform(m.begin(), m.end(), std::back_inserter(v), [] ( const std::pair< std::string,int > &it ) { return it.second; }); v.erase( std::remove_if( v.begin(), v.end(), [](const int value){ return (value % 2) != 0; }), v.end()); } clock_t end2 = clock(); std::cout << "Execution Time transform : " << (end2-start2) << std::endl; /////////// SIMPLE FOR LOOP //////////////////// clock_t start3 = clock(); for (unsigned k=0; k<10000; k++) { v.clear(); for (std::map< std::string, int >::iterator it = m.begin(); it != m.end(); ++it ) { if ((it->second % 2) == 0) v.push_back(it->second); } } clock_t end3=clock(); std::cout << "Execution Time Simple For Loop : " << (end3-start3) << std::endl; } 

The results obtained are as follows:

 Execution Time for_each : 7330000 Execution Time transform : 11090000 Execution Time Simple For Loop : 6530000 
+2
source share

std::copy_if will not allow you to switch from one type to another, only to filter what you need to copy.

You can use std::transform to get rid of the key, and then use std::remove_if :

  std::vector<int> v; std::transform(m.begin(), m.end(), std::back_inserter(v), [] ( const std::pair< std::string,int > &it ) { return it.second; }); v.erase( std::remove_if( v.begin(), v.end(), [](const int value){ return (value % 2) != 0; }), v.end()); 

However, a simple loop will be more efficient and much easier to read.

+2
source share

Presumably you just want to get the associated values ​​from map , not the keys.

In the SGI version of STL, there are select1st and select2nd iterators for this type of task.

Personally, however, I do not think that this should really be done with a copy - you convert the data, not copy it. Thus, I would advise using std::transform with a functor to return the second element in a pair.

0
source share

All Articles