简体   繁体   English

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

[英]Copy map values to vector in STL

Working my way through Effective STL at the moment.目前正在通过 Effective STL 工作。 Item 5 suggests that it's usually preferable to use range member functions to their single element counterparts.第 5 条建议使用范围成员函数而不是它们的单个元素对应物通常更可取。 I currently wish to copy all the values in a map (ie - I don't need the keys) to a vector.我目前希望将地图中的所有值(即 - 我不需要键)复制到向量中。

What is the cleanest way to do this?什么是最干净的方法来做到这一点?

You could probably use std::transform for that purpose.为此,您可能可以使用std::transform I would maybe prefer Neils version though, depending on what is more readable.不过,我可能更喜欢 Neils 版本,这取决于更易读的内容。


Example by xtofl (see comments): 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) );
}

Very generic, remember to give him credit if you find it useful.非常通用,如果觉得有用记得给他点赞。

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.您不能在此处轻松使用范围,因为您从映射中获得的迭代器指的是 std::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 );
    }
}

Old question, new answer.老问题,新答案。 With C++11 we have the fancy new for loop:在 C++11 中,我们有了新的 for 循环:

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

where schemas is a std::map and names is an std::vector .其中 schemas 是std::map , names 是std::vector

This populates the array (names) with keys from the map (schemas);这将使用地图(模式)中的键填充数组(名称); change s.first to s.second to get an array of values.s.first更改为s.second以获取值数组。

#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 ).此函数对inputIterator范围 ( beginInputRange - endInputRange ) 中的每个项目调用unaryOperation The value of operation is stored into outputIterator . operation 的值存储在outputIterator

If we want to operate through whole map - we use map.begin() and map.end() as our input range.如果我们想操作整个地图 - 我们使用 map.begin() 和 map.end() 作为我们的输入范围。 We want to store our map values into vector - so we have to use back_inserter on our vector: back_inserter(your_values_vector) .我们想将我们的地图值存储到向量中 - 所以我们必须在我们的向量上使用 back_inserter : back_inserter(your_values_vector) The back_inserter is special outputIterator that pushes new elements at the end of given (as paremeter) collection. back_inserter 是特殊的 outputIterator,它在给定(作为参数)集合的末尾推送新元素。 The last parameter is unaryOperation - it takes only one parameter - inputIterator's value.最后一个参数是 unaryOperation - 它只需要一个参数 - inputIterator 的值。 So we can use lambda: [](auto &kv) { [...] } , where &kv is just a reference to map item's pair.所以我们可以使用 lambda: [](auto &kv) { [...] } ,其中 &kv 只是对映射项对的引用。 So if we want to return only values of map's items we can simply return kv.second:因此,如果我们只想返回地图项的值,我们可以简单地返回 kv.second:

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

I think this explains any doubts.我认为这可以解释任何疑问。

If you are using the boost libraries , you can use boost::bind to access the second value of the pair as follows:如果您使用的是boost 库,则可以使用 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) );
}

This solution is based on a post from Michael Goldshteyn on the boost mailing list .此解决方案基于 Michael Goldshteyn 在boost 邮件列表上的一篇文章。

Using lambdas one can perform the following:使用 lambda 可以执行以下操作:

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

Here is what I would do.这就是我要做的。
Also I would use a template function to make the construction of select2nd easier.此外,我会使用模板函数使 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)
                  );
}

I thought it should be我以为应该是

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

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); auto vec = MapValuesAsVector(anymap);

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.我们应该使用 STL 算法中的变换函数,变换函数的最后一个参数可以是函数对象、函数指针或将映射项转换为向量项的 lambda 函数。 This case map have items have type pair that need to convert to item that has int type for vector.此案例映射具有类型对的项目,需要将其转换为具有 int 类型的矢量项。 Here is my solution that I use lambda function:这是我使用 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;
       });

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

The other answers mention std::transform , and semantically it's the right choice.其他答案提到std::transform ,从语义上讲,这是正确的选择。 But in practice std::accumulate might fit better for this task, because:但实际上std::accumulate可能更适合这项任务,因为:

  • it allows adding const to the resulting vector;它允许将const添加到结果向量中;
  • it just looks nicer, truly functional-style.它看起来更好,真正的功能风格。

Example (using C++17 syntax):示例(使用 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;
});

Surprised nobody has mentioned the most obvious solution , use the std::vector constructor.令人惊讶的是没有人提到最明显的解决方案,使用 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());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM