将地图值复制到STL中的矢量


85

目前,我正在通过有效的STL进行工作。第5项建议通常最好使用range成员函数而不是它们的单个元素对应项。我目前希望将地图中的所有值(即-我不需要键)复制到向量中。

什么是最干净的方法?


如果不需要键,则可能也不需要整个地图。在这种情况下,请考虑将值从映射移动到向量,如本问题所述
Nykodym

Answers:


61

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

78
Python确实把我宠坏了:-(
Gilad Naor

1
很好,模板。也许给它一个输出迭代器而不是一个容器!
xtofl

Skurmedel的解决方案更好:将ap-> p.second函子与'transform'函数一起使用。
xtofl

2
我坚信Occam的Razor-不要过多介绍实体。在转换解决方案的情况下,我们需要一个显式循环解决方案不需要的辅助函数。因此,在获得无名函数之前,我将坚持使用我的解决方案。

3
当心Occam的Razor解释。最后引入一个新的非常量变量“ it”可能不是最安全的解决方案。STL算法已被证明快速,可靠,已有相当长的一段时间了。
文森特·罗伯特,

62

您可能会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) );
}

非常通用,如果您觉得有用,请记住给他功劳。


我想甚至比尼尔。锻炼,锻炼!
xtofl

我建议对最后一个参数使用lambda。
varepsilon 2015年

@varepsilon:可能是一个好主意(如果使用现代C ++编译器),但是我对C ++不再有信心,这些天我有点C。如果有人想改进它并认为自己可以做到,请继续:)
Skurmedel 2015年

29

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

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

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

这会使用映射(方案)中的键填充数组(名称);更改s.firsts.second获取值数组。


3
应该是const auto &s
Slava

1
@Slava阐明了基于以下范围的任何新内容:我编写它的方式,但是,Slava建议的版本更快,更安全,因为避免了使用引用复制迭代器对象的情况,并指定了一个const,因为它将修改迭代器很危险。谢谢。
塞斯

4
最短最干净的解决方案。可能是最快的(经过测试,其速度比公认的解决方案还快,并且比@Aragornx的解决方案还快)。添加reserve(),您将获得另一个性能提升。随着C ++ 11的到来,现在应该成为公认的解决方案!
艾德里安W

3
这不应该是names.push_back(s.second); 正如问题所要求的是值,而不是向量中的键?
David

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

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


22
#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每个项目。操作值存储在中。inputIteratorbeginInputRangeendInputRangeoutputIterator

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

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

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


3
嗨,请在代码中添加一些解释,因为它有助于理解您的代码。只回答代码的人不满意。
巴尔加夫Rao

1
是! 此代码段可能会解决问题,包括说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
J. Chomel

我认为这仅从C ++ 14开始有效,因为在此之前lambda不支持auto。显式函数签名仍将起作用。
图罗尼

19

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

1
我认为您不需要v.reserve(m.size()),因为v随着push_back新元素的增加而增长。
DraganOstojić16年

11
@DraganOstojić.reserve()仅导致一个重新分配。根据元素的数量,.push_back()可能会执行多个分配以达到相同的大小。
mskfisher

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

1
好一个 为什么make_select2nd不在stl中?
Mykola Golubyev 09年

select2nd是SGI版本中STL的扩展(因此是非官方的)。现在将功能模板添加为实用程序只是第二天的习惯(有关灵感,请参见make_pair <>())。
马丁·约克2009年

2

一种方法是使用仿函数:

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

我不会理会变量aConverter。只需在for_each中创建一个临时目录。std :: for_each(myMap.begin(),myMap.end(),CopyMapToVec <std :: string,int>(myVector));
马丁·约克

最好使用“变换”,因为这就是您正在做的事情:使用非常简单的函子将地图变换为向量。
xtofl

2

为什么不:

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

用法:

自动vec = MapValuesAsVector(anymap);


我认为您的vec将是地图
dyomas'Aug

谢谢dyomas,我已经更新了功能以做一个保留而不是调整大小,现在它可以正常工作了
Jan Wilmans

2

我以为应该

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

2

我们应该使用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;
       });

-3

惊讶的是没有人提到最明显的解决方案,请使用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());
}

4
那是因为您的解决方案不适合这个问题。向量应仅包含值。
ypnos
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.