Copy map values to vector in STL

C++StlContainers

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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 );
	}
}

Solution 3 - C++

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.

Solution 4 - C++

#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.

Solution 5 - C++

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.

Solution 6 - C++

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); });
}

Solution 7 - C++

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)
                  );
}

Solution 8 - C++

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);
}

Solution 9 - C++

I thought it should be

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

Solution 10 - C++

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);

Solution 11 - C++

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;
       });

Solution 12 - C++

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;
});

Solution 13 - C++

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());
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGilad NaorView Question on Stackoverflow
Solution 1 - C++SkurmedelView Answer on Stackoverflow
Solution 2 - C++anonView Answer on Stackoverflow
Solution 3 - C++SethView Answer on Stackoverflow
Solution 4 - C++AragornxView Answer on Stackoverflow
Solution 5 - C++OK.View Answer on Stackoverflow
Solution 6 - C++Matthieu N.View Answer on Stackoverflow
Solution 7 - C++Martin YorkView Answer on Stackoverflow
Solution 8 - C++aJ.View Answer on Stackoverflow
Solution 9 - C++OJMANView Answer on Stackoverflow
Solution 10 - C++Jan WilmansView Answer on Stackoverflow
Solution 11 - C++Loc TranView Answer on Stackoverflow
Solution 12 - C++Michael D.View Answer on Stackoverflow
Solution 13 - C++franjesusView Answer on Stackoverflow