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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/771453/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:12:53  来源:igfitidea点击:

Copy map values to vector in STL

c++stlcontainers

提问by Gilad Naor

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.

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

What is the cleanest way to do this?

什么是最干净的方法来做到这一点?

采纳答案by Gilad Naor

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.

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

回答by Skurmedel

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

您可能会std::transform为此目的使用。不过,我可能更喜欢 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.

非常通用,如果觉得有用记得给他点赞。

回答by Seth

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::mapand names is an std::vector.

其中 schemas 是 a std::map, names 是 an std::vector

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

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

回答by OK.

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 邮件列表上的一篇文章。

回答by Aragornx

#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 unaryOperationon every item from inputIteratorrange (beginInputRange-endInputRange). The value of operation is stored into outputIterator.

此函数调用范围 ( - ) 中的unaryOperation每个项目。操作的值存储到.inputIteratorbeginInputRangeendInputRangeoutputIterator

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:

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

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

I think this explains any doubts.

我认为这可以解释任何疑问。

回答by Aragornx

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

回答by Martin York

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

回答by OJMAN

I thought it should be

我以为应该是

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

回答by Jan Wilmans

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

回答by aJ.

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