views:

2891

answers:

7

Working my way through Effective STL at the moment. Item 5 suggests that it's usually preferable to use range member functions to their single element counterparts. I currently wish to copy all the values in a map (i.e. - I don't need the keys) to a vector.

What is the cleanest way to do this?

+7  A: 

You can't easily use a range here becuase the iterator you get from a map refers to a std::pair, where the iterators you would use to insert into a vector refers to an object of the type stored in the vector, which is (if you are discarding the key) not a pair.

I really don't think it gets much cleaner than the obvious:

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

int main() {
    typedef map <string, int> MapType;
    MapType m; 
    vector <int> v;

    // populate map somehow

    for( MapType::iterator it = m.begin(); it != m.end(); ++it ) {
     v.push_back( it->second );
    }
}

which I would probably re-write as a template function if I was going to use it more than once. Something like:

template <typename M, typename V> 
void MapToVec( const  M & m, V & v ) {
    for( typename M::const_iterator it = m.begin(); it != m.end(); ++it ) {
     v.push_back( it->second );
    }
}
anon
Python has truly spoiled me :-(
Gilad Naor
Nice, the template. Maybe give it an output iterator instead of a container!
xtofl
Skurmedel's solution is even nicer: use the 'transform' function with a p -> p.second functor.
xtofl
I'm a firm believer in Occam's Razor - don't introduce entities unecessarily. In the case of the transform solution, we need a subsidiary function which is not needed on the explicit loop solution. So until we get nameless functions, I'll stick with my solution.
anon
Beware of Occam's Razor interpretation. Introducing a new non-const variable "it" may not the safest solution in the end. STL algorithms have been proven fast and robust for quite some time now.
Vincent Robert
But as soon as lambda functions are there, it will be the simplest solution.
tstenner
@vincent but the scope of the variable is limited to the function - if I could limit the scope of the subsidiary function (as I will be able to, eventually) then I would use the transform approach myself - until then, not.
anon
@tstenner - unfortunately, we have to program in the here and now.
anon
@neil Actually, you could use an anonymous namespace in order to hide the functors from the outside world.
Vincent Robert
+8  A: 

You could probably use transform for that purpose. I would maybe prefer Neils version though, depending on what is more readable.


Example by xtofl (see comments):

#include <map>
#include <vector>
#include <algorithm>
#include <iostream>

template< typename tPair >
struct second_t {
    typename tPair::second_type operator()( const tPair& p ) const { return     p.second; }
};

template< typename tMap > 
second_t< typename tMap::value_type > second( const tMap& m ) { return second_t<     typename tMap::value_type >(); }


int main() {
    std::map<int,bool> m;
    m[0]=true;
    m[1]=false;
    //...
    std::vector<bool> v;
    std::transform( m.begin(), m.end(), std::back_inserter( v ), second(m) );
    std::transform( m.begin(), m.end(), std::ostream_iterator<bool>( std::cout,     ";" ), second(m) );
}

Very generic, remember to give him credit if you find it useful.

Skurmedel
_that_ I like even better than Neil's. Workidout, workidout!
xtofl
(example: http://codepad.org/4kBV9Rg2 )
xtofl
Neat super-generic code xtofl.
Skurmedel
+1 Add the call to reserve on the vector and it's perfect.
lothar
Care to give me a reason for downvote?
Skurmedel
A: 

One way is to use functor:

 template <class T1, class T2>
    class CopyMapToVec
    {
    public: 
        CopyMapToVec(std::vector<T2>& aVec): mVec(aVec){}

        bool operator () (const std::pair<T1,T2>& mapVal) const
        {
         mVec.push_back(mapVal.second);
         return true;
        }
    private:
        std::vector<T2>& mVec;
    };


int main()
{
    std::map<std::string, int> myMap;
    myMap["test1"] = 1;
    myMap["test2"] = 2;

    std::vector<int>  myVector;

    //reserve the memory for vector
    myVector.reserve(myMap.size());
    //create the functor
    CopyMapToVec<std::string, int> aConverter(myVector);

    //call the functor
    std::for_each(myMap.begin(), myMap.end(), aConverter);
}
aJ
I would not bother with the variable aConverter. just create a temporary in the for_each. std::for_each(myMap.begin(), myMap.end(), CopyMapToVec<std::string, int>(myVector));
Martin York
prefer 'transform', since that's what you're doing: transforming a map into a vector using a quite straightforward functor.
xtofl
A: 

Here is what I would do.
Also I would use a template function to make the construction of select2nd easier.

#include <map>
#include <vector>
#include <algorithm>
#include <memory>
#include <string>

/*
 * A class to extract the second part of a pair
 */   
template<typename T>
struct select2nd
{
    typename T::second_type operator()(T const& value) const
    {return value.second;}
};

/*
 * A utility template function to make the use of select2nd easy.
 * Pass a map and it automatically creates a select2nd that utilizes the
 * value type. This works nicely as the template functions can deduce the
 * template parameters based on the function parameters. 
 */
template<typename T>
select2nd<typename T::value_type> make_select2nd(T const& m)
{
    return select2nd<typename T::value_type>();
}

int main()
{
    std::map<int,std::string>   m;
    std::vector<std::string>    v;

    /*
     * Please note: You must use std::back_inserter()
     *              As transform assumes the second range is as large as the first.
     *              Alternatively you could pre-populate the vector.
     *
     * Use make_select2nd() to make the function look nice.
     * Alternatively you could use:
     *    select2nd<std::map<int,std::string>::value_type>()
     */   
    std::transform(m.begin(),m.end(),
                   std::back_inserter(v),
                   make_select2nd(m)
                  );
}
Martin York
Good one. And why make_select2nd are not in the stl?
Mykola Golubyev
select2nd is an extension to the STL in the SGI version (so unofficial). Adding function templates as utilities is just second nature now (see make_pair<>() for inspiration).
Martin York
+4  A: 

Using lambdas one can perform the following:

{
       std::map<std::string,int> m;
       std::vector<int> v;
       v.reserve(m.size());
       std::for_each(m.begin(),m.end(),
                    [](const std::map<std::string,int>::value_type& p) 
                    { 
                       v.push_back(p.second);
                    });
}
Beh Tou Cheh
+2  A: 

If you are using the boost librarys, you can use boost::bind to access the second value of the pair as follows:

#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <boost/bind.hpp>

int main()
{
   typedef std::map<std::string, int> MapT;
   typedef std::vector<int> VecT;
   MapT map;
   VecT vec;

   map["one"] = 1;
   map["two"] = 2;
   map["three"] = 3;
   map["four"] = 4;
   map["five"] = 5;

   std::transform( map.begin(), map.end(),
                   std::back_inserter(vec),
                   boost::bind(&MapT::value_type::second,_1) );
}

This solution is based on a post from Michael Goldshteyn on the boost mailing list.

OK
+1  A: 

I thought it should be

std::transform( map.begin(), map.end(), std::back_inserter(vec), boost::bind(&MapT::value_type::first,_1) );

OJMAN