将映射值复制到STL中的向量

时间:2009-04-21 07:30:43

标签: c++ stl containers

目前正在通过Effective STL工作。第5项建议通常最好将范围成员函数用于它们的单个元素对应物。我目前希望将地图中的所有值(即 - 我不需要键)复制到矢量。

最干净的方法是什么?

13 个答案:

答案 0 :(得分:50)

你不能轻易地使用一个范围,因为你从地图中得到的迭代器引用了一个std :: pair,你用来插入向量的迭代器引用了一个存储在向量中的类型的对象,(如果你丢弃钥匙)不是一对。

我真的不认为它比明显更清洁:

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

如果我不止一次使用它,我可能会重新编写为模板函数。类似的东西:

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

答案 1 :(得分:49)

您可以将std::transform用于此目的。我可能更喜欢Neils版本,具体取决于更具可读性。


xtofl示例(见评论):

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

非常通用,如果你发现它有用,请记得给予他信任。

答案 2 :(得分:23)

如果您使用boost libraries,可以使用boost :: bind访问该对的第二个值,如下所示:

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

此解决方案基于Michael Goldshteyn在boost mailing list上的帖子。

答案 3 :(得分:21)

老问题,新答案。使用C ++ 11,我们有了新的for循环:

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

其中架构为std::map,名称为std::vector

这将使用地图中的键(模式)填充数组(名称);将s.first更改为s.second以获取值数组。

答案 4 :(得分:18)

使用lambdas可以执行以下操作:

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

答案 5 :(得分:12)

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

很抱歉我没有添加任何解释 - 我认为代码非常简单,不需要任何解释。 所以:

transform( beginInputRange, endInputRange, outputIterator, unaryOperation)

此函数会在unaryOperation范围(inputIterator - beginInputRange)的每个项目上调用endInputRange。操作值存储在outputIterator

如果我们想要通过整个地图进行操作 - 我们使用map.begin()和map.end()作为输入范围。我们想将地图值存储到矢量中 - 所以我们必须在矢量上使用back_inserter:back_inserter(your_values_vector)。 back_inserter是特殊的outputIterator,它在给定(作为参数)集合的末尾推送新元素。 最后一个参数是unaryOperation - 它只需要一个参数 - inputIterator的值。所以我们可以使用lambda: [](auto &kv) { [...] },其中&amp; kv只是对地图项目对的引用。因此,如果我们只想返回map的项的值,我们可以简单地返回kv.second:

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

我认为这解释了任何疑问。

答案 6 :(得分:8)

这是我要做的 我还会使用模板函数来简化select2nd的构造。

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

答案 7 :(得分:2)

我认为应该是

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

答案 8 :(得分:1)

一种方法是使用仿函数:

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

答案 9 :(得分:1)

为什么不:

cmd

用法:

auto vec = MapValuesAsVector(anymap);

答案 10 :(得分:1)

我们应该使用STL算法的变换函数,变换函数的最后一个参数可以是将映射项转换为向量项的函数对象,函数指针或lambda函数。此案例图的项目具有类型对,需要将其转换为矢量为int类型的项目。这是我使用lambda函数的解决方案:

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

答案 11 :(得分:1)

其他答案提到了 std::transform,从语义上讲这是正确的选择。但实际上,std::accumulate 可能更适合这项任务,因为:

  • 它允许将 const 添加到结果向量中;
  • 它看起来更漂亮,真正的功能风格。

示例(使用 C++17 语法):

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

答案 12 :(得分:-2)

令人惊讶的是没有人提到the most obvious solution,请使用std :: vector构造函数。

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