98

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?

Gilad Naor
  • 20,752
  • 14
  • 46
  • 53
  • If the keys are not needed, the whole map might not be needed as well. In such case consider moving the values from the map to the vector as described in this [question](https://stackoverflow.com/q/42282382/8805602). – Nykodym Jan 15 '20 at 21:53

13 Answers13

72

You could probably use std::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.

Kevin
  • 16,549
  • 8
  • 60
  • 74
Skurmedel
  • 21,515
  • 5
  • 53
  • 66
  • I would suggest to use lambda for the last parameter. – varepsilon Sep 25 '15 at 12:24
  • @varepsilon: Probably a good idea (if one is on a modern C++ compiler), but I'm not that confident with C++ anymore, I'm kinda a C dude these days. If anyone wants to improve it and thinks they can do it, please go ahead :) – Skurmedel Oct 01 '15 at 17:18
68

You can't easily use a range here because 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 );
    }
}
kevinarpe
  • 20,319
  • 26
  • 127
  • 154
  • 100
    Python has truly spoiled me :-( – Gilad Naor Apr 21 '09 at 07:47
  • 2
    Nice, the template. Maybe give it an output iterator instead of a container! – xtofl Apr 21 '09 at 08:14
  • Skurmedel's solution is even nicer: use the 'transform' function with a p -> p.second functor. – xtofl Apr 21 '09 at 08:16
  • 3
    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. –  Apr 21 '09 at 08:19
  • 3
    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 Apr 21 '09 at 08:53
  • But as soon as lambda functions are there, it will be the simplest solution. – tstenner Apr 21 '09 at 08:54
  • @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. –  Apr 21 '09 at 08:56
  • @tstenner - unfortunately, we have to program in the here and now. –  Apr 21 '09 at 08:57
  • @neil Actually, you could use an anonymous namespace in order to hide the functors from the outside world. – Vincent Robert Apr 22 '09 at 16:36
  • With C++11, there is now a more elegant solution with range-based for. See @Seth's answer. But also the traditional approach with iterator leaves room form improvement: `m.end()` does not change during the loop. You should cache it in a variable. – Adrian W Oct 11 '17 at 18:24
  • @GiladNaor, the first option here is rather pythonic: http://www.techiedelight.com/convert-map-vector-key-value-pairs-cpp/ – lanwatch Aug 28 '18 at 15:32
  • Excuse me, doesn't `++it` in the for loop mean that the code skips the first element in the map `m`, and at the end attempts to access `m.end()->second`? – Kagaratsch Feb 24 '21 at 17:48
  • why do you not use reserve? – IkarusDeveloper Nov 05 '21 at 12:48
47

With C++11 we have the fancy new for loop:

for (const auto &s : schemas)
   names.push_back(s.second);

where schemas is a std::map and names is an std::vector.

This populates the array (names) with values from the map (schemas); change s.second to s.first to get an array of keys.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Seth
  • 708
  • 6
  • 8
  • 8
    Shortest and cleanest solution. And probably the fastest (tested to be faster than the accepted solution and also faster than @Aragornx's solution). Add `reserve()` and you'll get another performance gain. With the advent of C++11 that should now be the accepted solution! – Adrian W Oct 11 '17 at 18:18
  • Agree, this is the most clear, simple and self explaining solution (with reserve()). Algorithms, lambdas etc. here is an overkill (killing a fly with hammer...). – Niki Jun 01 '22 at 09:34
36
#include <algorithm> // std::transform
#include <iterator>  // std::back_inserter
std::transform( 
    your_map.begin(), 
    your_map.end(),
    std::back_inserter(your_values_vector),
    [](auto &kv){ return kv.second;} 
);

Sorry that I didn't add any explanation - I thought that code is so simple that is doesn't require any explanation. So:

transform( beginInputRange, endInputRange, outputIterator, unaryOperation)

this function calls unaryOperation on every item from inputIterator range (beginInputRange-endInputRange). The value of operation is stored into outputIterator.

If we want to operate through whole map - we use map.begin() and map.end() as our input range. We want to store our map values into vector - so we have to use back_inserter on our vector: back_inserter(your_values_vector). The back_inserter is special outputIterator that pushes new elements at the end of given (as paremeter) collection. The last parameter is unaryOperation - it takes only one parameter - inputIterator's value. So we can use lambda: [](auto &kv) { [...] }, where &kv is just a reference to map item's pair. So if we want to return only values of map's items we can simply return kv.second:

[](auto &kv) { return kv.second; }

I think this explains any doubts.

Adrian W
  • 4,563
  • 11
  • 38
  • 52
Aragornx
  • 429
  • 5
  • 10
  • 3
    Hi, do add a bit of explanation along with the code as it helps to understand your code. Code only answers are frowned upon. – Bhargav Rao Sep 23 '16 at 07:53
  • 1
    Yes! this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – J. Chomel Sep 23 '16 at 08:51
  • I think this only works starting from C++14, since auto isn't supported in lambda's prior to that. Explicit function signature would still work. – turoni Jul 30 '18 at 06:28
24

If you are using the boost libraries, 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.

Dmitri Nesteruk
  • 23,067
  • 22
  • 97
  • 166
OK.
  • 2,374
  • 2
  • 17
  • 20
20

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(),
                 [&v](const std::map<std::string,int>::value_type& p) 
                 { v.push_back(p.second); });
}
  • 1
    I don't think you need to v.reserve(m.size()) because v will grow as you push_back new elements. – Dragan Ostojić Sep 28 '16 at 18:37
  • 11
    @DraganOstojić .reserve() only causes one reallocation. Depending on the number of elements, .push_back() may perform multiple allocations to get to the same size. – mskfisher Mar 14 '17 at 20:52
9

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
  • 257,169
  • 86
  • 333
  • 562
2

I thought it should be

std::transform( map.begin(), map.end(), 
                   std::back_inserter(vec), 
                   boost::bind(&MapT::value_type::first,_1) ); 
Nick
  • 138,499
  • 22
  • 57
  • 95
OJMAN
  • 21
  • 1
2

Why not:

template<typename K, typename V>
std::vector<V> MapValuesAsVector(const std::map<K, V>& map)
{
   std::vector<V> vec;
   vec.reserve(map.size());
   std::for_each(std::begin(map), std::end(map),
        [&vec] (const std::map<K, V>::value_type& entry) 
        {
            vec.push_back(entry.second);
        });
    return vec;
}

usage:

auto vec = MapValuesAsVector(anymap);

Jan Wilmans
  • 665
  • 6
  • 10
2

We should use the transform function from STL algorithm, the last parameter of transform function could be a function object, function pointer or a lambda function that convert item of map to item of vector. This case map have items have type pair that need to convert to item that has int type for vector. Here is my solution that I use lambda function:

#include <algorithm> // for std::transform
#include <iterator>  // for back_inserted

// Map of pair <int, string> need to convert to vector of string
std::map<int, std::string> mapExp = { {1, "first"}, {2, "second"}, {3, "third"}, {4,"fourth"} };

// vector of string to store the value type of map
std::vector<std::string> vValue;

// Convert function
std::transform(mapExp.begin(), mapExp.end(), std::back_inserter(vValue),
       [](const std::pair<int, string> &mapItem)
       {
         return mapItem.second;
       });
Loc Tran
  • 1,170
  • 7
  • 15
2

The other answers mention std::transform, and semantically it's the right choice. But in practice std::accumulate might fit better for this task, because:

  • it allows adding const to the resulting vector;
  • it just looks nicer, truly functional-style.

Example (using C++17 syntax):

#include <numeric> // for std::accumulate. Note that it's not in <algorithm> where std::transform is located, thanks to Anton Krug for pointing this out

auto map = std::map<int,bool>{};
map[0]=true;
map[1]=false;

const auto mapValues = std::accumulate(map.begin(), map.end(), std::vector<bool>(map.size()), [](auto& vector, const auto& mapEntry) {
    vector.push_back(mapEntry.second);
    return vector;
});
Michael D.
  • 83
  • 7
  • That is not fully correct, your code wouldn't compile, here is compiling version: https://godbolt.org/z/hMafr6jzE BTW accumulate is in the and not in the . The transform is in the so this is giving misleading information: https://en.cppreference.com/w/cpp/algorithm/accumulate – Anton Krug Apr 15 '21 at 14:17
2

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.
  • 34,624
  • 22
  • 86
  • 128
  • I would not bother with the variable aConverter. just create a temporary in the for_each. std::for_each(myMap.begin(), myMap.end(), CopyMapToVec(myVector)); – Martin York Apr 21 '09 at 08:46
  • prefer 'transform', since that's what you're doing: transforming a map into a vector using a quite straightforward functor. – xtofl Apr 21 '09 at 08:56
-3

Surprised nobody has mentioned the most obvious solution, use the std::vector constructor.

template<typename K, typename V>
std::vector<std::pair<K,V>> mapToVector(const std::unordered_map<K,V> &map)
{
    return std::vector<std::pair<K,V>>(map.begin(), map.end());
}
lanwatch
  • 1,377
  • 2
  • 9
  • 9
  • 4
    That's because your solution does not fit the question. The vector should consist of only the values. – ypnos Oct 05 '18 at 16:50