【问题标题】:Getting a list of values from a map从地图中获取值列表
【发布时间】:2011-05-10 21:13:44
【问题描述】:

有没有一种从地图中获取值列表的 stl 方法?

也就是说,我有:

std::map<A,B> myMap;

我想要一个只返回值列表的函数,即std::list&lt;B&gt;(或为此设置。 有没有内置的 stl 方法可以做到这一点?

【问题讨论】:

  • 不同意 dup,因为 IMO 接受的答案不是我认为的最佳设计选择。
  • 实际上,这并没有改变这个问题是重复的事实,它只是意味着你应该回答第一个问题而不是第二个问题,以避免在 SO 上分散答案。不过,我确实喜欢你的回答。

标签: c++ list stl map


【解决方案1】:

map 元素定义为map::value_type,其类型为pair&lt;A,B&gt;first 是键,second 是值。您可以编写functor 以从value_type 中提取second,并将其复制到vector(或list,或任何您想要的。)进行复制的最佳方法是使用transform,正如它的名字所暗示的那样:它接受一种类型的值并将其转换为另一种类型的值。

这是一个完整的工作示例:

#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <vector>
#include <iostream>
using namespace std;

typedef map<unsigned, string> MyMap;
MyMap my_map;

struct get_second : public std::unary_function<MyMap::value_type, string>
{
    string operator()(const MyMap::value_type& value) const
    {
        return value.second;
    }
};

int main()
{
    my_map[1] = "one";
    my_map[2] = "two";
    my_map[3] = "three";
    my_map[4] = "four";
    my_map[5] = "five";

    // get a vector of values
    vector<string> my_vals;
    transform(my_map.begin(), my_map.end(), back_inserter(my_vals), get_second() );

    // dump the list
    copy( my_vals.begin(), my_vals.end(), ostream_iterator<string>(cout, "\n"));
}

编辑:

如果你有一个支持 C++0x lambdas 的编译器,你可以完全消除仿函数。这对于使代码更具可读性、可争论性、更易于维护非常有用,因为您最终不会在代码库中漂浮着几十个小的一次性仿函数。以下是您将如何更改上面的代码以使用 lambda:

transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const MyMap::value_type& val){return val.second;} );

【讨论】:

  • 更简洁:transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const auto& val){return val.second;} );
【解决方案2】:

没有内置任何东西,没有。不过,编写自己的函数很简单:迭代地图。迭代器会给你一个pair&lt;A, B&gt;。将每个 second 值添加到结果列表中。

【讨论】:

  • 我会考虑 transform 和仿函数的想法或多或少是内置的
  • @约翰。通过说“内置”,我将问题解释为请求 PHP's array_valuesPerl's values — 该库提供了一个完全针对任务量身定制的功能,而不仅仅是一堆你必须放在一起编写 @987654328 的部分@你自己的函数。如果您的编译器或库没有提供所有最好的部分(如 select2nd 或 lambdas),情况会更糟。
【解决方案3】:

您不能仅仅“获取”这样一个列表,因为没有预先存在的列表存储在胆量中的任何位置,但您可以构建一个:

typedef std::map<A,B> myMapType;
myMapType myMap;
std::list<B> valueList;
for (myMapType::const_iterator it=myMap.begin(); it!=myMap.end(); ++it) {
  valueList.push_back( it->second );
}

或者如果你真的更喜欢 STL 方式:

class GetSecond {
  template<typename T1, typename T2>
  const T2& operator()( const std::pair<T1,T2>& key_val ) const
    { return key_val.second; }
};

typedef std::map<A,B> myMapType;
myMapType myMap;
std::list<B> valueList;
std::transform(myMap.begin(), myMap.end(), std::back_inserter(valueList),
               GetSecond());

【讨论】:

  • 很遗憾,C++ 标准库中没有select2nd
  • 是的,我发现得太晚了。我将不再为该死的 SGI“文档”而烦恼。
【解决方案4】:

许多“内置”方式中的一种当然是最明显的一种。只需遍历按键 (pair::first) 排序的所有对元素,并将值 (pair::second) 添加到新容器中,您可以使用正确的容量构造该容器以在迭代期间消除多余的分配,并添加。

请注意:std::list 很少是您真正想要使用的容器。当然,除非您真的非常确实需要它的特定功能。

【讨论】:

    【解决方案5】:

    当然。

    std::list<B> list;
    std::for_each(myMap.begin(), myMap.end(), [&](const std::pair<const A, B>& ref) {
        list.push_back(ref.second);
    });
    

    如果你没有 C++0x 编译器,首先你有我的同情,其次,你需要为此目的构建一个快速函数对象。

    【讨论】:

    • std::transform 的爱在哪里?
    • @DeadMG: lambdas 超出了域,因为这个问题没有被标记 [c++0x]
    • @DeadMG:不过,我会修改我的答案以包含一个 lamda 示例
    • @John Dibling:当 MSVC10 和 GCC4.5 都支持 Lambda 支持时,不再需要显式标记它们。
    • @DeadMG:真的吗?即使C++和C++0x不一样?
    【解决方案6】:

    你可以使用boost的transform_iterator:http://www.boost.org/doc/libs/1_64_0/libs/iterator/doc/transform_iterator.html

    struct GetSecond {
      template <typename K, typename T>
      const T& operator()(const std::pair<K, T> & p) const { return p.second; }
      template <typename K, typename T>
      T& operator()(std::pair<K, T> & p) const { return p.second; }
    };
    
    template <typename MapType>
      auto begin_values(MapType& m) -> decltype(boost::make_transform_iterator(m.begin(), GetSecond())) {
      return boost::make_transform_iterator(m.begin(), GetSecond());
    }
    
    template <typename MapType>
      auto end_values(MapType& m) -> decltype(boost::make_transform_iterator(m.end(), GetSecond())) {
      return boost::make_transform_iterator(m.end(), GetSecond());
    }
    
    template <typename MapType>
      struct MapValues {
      MapType & m;
      MapValues(MapType & m) : m(m) {}
      typedef decltype(begin_values(m)) iterator;
      iterator begin() { return begin_values(m); }
      iterator end() { return end_values(m); }
    };
    
    template <typename MapType>
      MapValues<MapType> get_values(MapType & m) {
      return MapValues<MapType>(m);
    }
    
    
    int main() {
      std::map<int, double> m;
      m[0] = 1.0;
      m[10] = 2.0;
      for (auto& x : get_values(m)) {
        std::cout << x << ',';
        x += 1;
      }
      std::cout << std::endl;
      const std::map<int, double> mm = m;
      for (auto& x : get_values(mm)) {
        std::cout << x << ',';
      }
      std::cout << std::endl;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-16
      • 2017-06-20
      • 2022-08-17
      • 2021-08-06
      • 2021-03-17
      • 2014-08-07
      • 2023-01-24
      • 2021-04-24
      • 1970-01-01
      相关资源
      最近更新 更多