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

Gil*_*aor 72 c++ containers stl

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

最干净的方法是什么?

小智 55

你不能轻易地使用一个范围,因为你从地图中得到的迭代器引用了一个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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我不止一次使用它,我可能会重新编写为模板函数.就像是:

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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • Python真的宠坏了我:-( (69认同)
  • 谨防奥卡姆的剃刀解释.引入一个新的非常量变量"它"可能不是最安全的解决方案.STL算法已被证明在一段时间内快速而稳健. (3认同)
  • 不错,模板。也许给它一个输出迭代器而不是一个容器! (2认同)
  • 我坚信Occam的剃刀 - 不要不必要地介绍实体.在转换解决方案的情况下,我们需要一个在显式循环解决方案中不需要的辅助功​​能.所以在我们获得无名功能之前,我会坚持使用我的解决方案. (2认同)

Sku*_*del 54

你可以用它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) );
}
Run Code Online (Sandbox Code Playgroud)

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


OK.*_*OK. 24

如果您正在使用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) );
}
Run Code Online (Sandbox Code Playgroud)

此解决方案基于Michael Goldshteyn在boost邮件列表中的帖子.


Set*_*eth 22

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

for (const auto &s : schemas)
   names.push_back(s.first);
Run Code Online (Sandbox Code Playgroud)

模式是a std::map,名称是std::vector.

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

  • 它应该是`const auto&s` (2认同)
  • @Slava 澄清基于以下范围的任何新人:我编写它的工作方式,但是,Slava 建议的版本更快、更安全,因为避免了使用引用复制迭代器对象,并指定了一个常量,因为它将是修改迭代器很危险。谢谢。 (2认同)
  • 最短最干净的解决方案。而且可能是最快的(经测试,它比公认的解决方案还快,也比@Aragornx的解决方案快)。添加`reserve()`,您将获得另一个性能提升。随着C ++ 11的出现,现在应该是公认的解决方案! (2认同)
  • 这不应该是names.push_back(s.second); 因为问题要求值,而不是向量中的键? (2认同)

小智 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); });
}
Run Code Online (Sandbox Code Playgroud)

  • @DraganOstojić.reserve()只会导致一次重新分配.根据元素的数量,.push_back()可以执行多次分配以达到相同的大小. (9认同)

小智 14

#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;} 
);
Run Code Online (Sandbox Code Playgroud)

对不起,我没有添加任何解释 - 我认为代码非常简单,不需要任何解释.所以:

transform( beginInputRange, endInputRange, outputIterator, unaryOperation)
Run Code Online (Sandbox Code Playgroud)

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

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

[](auto &kv) { return kv.second; }
Run Code Online (Sandbox Code Playgroud)

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

  • 嗨,请在代码中添加一些解释,因为它有助于理解您的代码.代码唯一的答案是不受欢迎的. (3认同)

Mar*_*ork 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)
                  );
}
Run Code Online (Sandbox Code Playgroud)