【问题标题】:Copy map values to vector in STL [duplicate]将地图值复制到 STL 中的向量
【发布时间】:2010-10-20 18:20:18
【问题描述】:

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

最干净的方法是什么?

【问题讨论】:

  • 如果不需要键,则可能也不需要整个地图。在这种情况下,请考虑将值从地图移动到矢量,如question 中所述。

标签: c++ stl containers


【解决方案1】:

您可能会为此目的使用std::transform。不过,我可能更喜欢 Neils 版本,这取决于更具可读性。


xtofl 的示例(参见 cmets):

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

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

【讨论】:

  • 那个我比尼尔更喜欢。锻炼,锻炼!
  • 我建议使用 lambda 作为最后一个参数。
  • @varepsilon:可能是个好主意(如果使用现代 C++ 编译器),但我对 C++ 不再那么自信了,这些天我有点像 C 哥们。如果有人想改进它并认为他们可以做到,请继续:)
【解决方案2】:

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

【讨论】:

  • Python 真的把我宠坏了 :-(
  • 很好,模板。也许给它一个输出迭代器而不是一个容器!
  • Skurmedel 的解决方案更好:使用带有 p -> p.second 函子的“转换”函数。
  • 我坚信奥卡姆剃刀法则——不要不必要地引入实体。在转换解决方案的情况下,我们需要一个在显式循环解决方案中不需要的辅助功​​能。所以在我们得到无名函数之前,我会坚持我的解决方案。
  • 谨防奥卡姆剃刀解释。引入一个新的非常量变量“it”最终可能不是最安全的解决方案。 STL 算法在相当长一段时间内已被证明是快速且稳健的。
【解决方案3】:

老问题,新答案。在 C++11 中,我们有了花哨的新 for 循环:

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

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

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

【讨论】:

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

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

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

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

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

【讨论】:

  • 您好,请在代码中添加一些解释,因为它有助于理解您的代码。仅代码的答案不受欢迎。
  • 是的!此代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
  • 我认为这只适用于从 C++14 开始,因为在此之前的 lambda 不支持 auto 。显式函数签名仍然有效。
【解决方案5】:

如果您使用的是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 上的帖子。

【讨论】:

    【解决方案6】:

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

    【讨论】:

    • 我认为你不需要 v.reserve(m.size()) 因为 v 会随着你 push_back 新元素的增长而增长。
    • @DraganOstojić .reserve() 只会导致一次重新分配。根据元素的数量,.push_back() 可能会执行多次分配以达到相同的大小。
    【解决方案7】:

    这就是我要做的。
    另外我会使用模板函数来简化 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)
                      );
    }
    

    【讨论】:

    • 好一个。为什么 make_select2nd 不在 stl 中?
    • select2nd 是 SGI 版本中 STL 的扩展(非官方)。将函数模板添加为实用程序现在只是第二天性(请参阅 make_pair() 以获得灵感)。
    【解决方案8】:

    一种方法是使用仿函数:

     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<:string int>(myVector));
    • 更喜欢“转换”,因为这就是您正在做的事情:使用非常简单的函子将地图转换为矢量。
    【解决方案9】:

    为什么不:

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

    用法:

    auto vec = MapValuesAsVector(anymap);

    【讨论】:

    • 我认为你的 vec 将是 map 的两倍
    • 感谢 dyomas,我已经更新了函数来进行保留而不是调整大小,现在它可以正常工作了
    【解决方案10】:

    我认为应该是

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

    【讨论】:

      【解决方案11】:

      我们应该使用STL算法中的transform函数,transform函数的最后一个参数可以是函数对象、函数指针或lambda函数,将map项转换为向量项。此案例映射具有类型对的项目,需要将其转换为具有 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;
             });
      

      【讨论】:

        【解决方案12】:

        其他答案提到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;
        });
        

        【讨论】:

        【解决方案13】:

        很惊讶没有人提到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());
        }
        

        【讨论】:

        • 那是因为你的解决方案不适合这个问题。向量应该只包含值。
        猜你喜欢
        • 2015-11-13
        • 1970-01-01
        • 1970-01-01
        • 2010-10-02
        • 1970-01-01
        • 2016-01-17
        • 2011-05-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多