从映射中获取值列表

16

有没有STL方法可以从map中获取值的列表?

比如说,我有:

std::map<A,B> myMap;

我希望有一个函数,可以返回值列表,例如std::list<B>(或者也可以是set)。是否有内置的STL方法可以做到这一点?


1
复制STL中的映射值到向量的副本 - James McNellis
我不同意dup,因为在我看来,那里接受的答案并不是我认为最好的设计选择。 - John Dibling
1
实际上,这并不改变这个问题是重复的事实,它只意味着你应该回答第一个问题而不是第二个问题,以避免在 SO 上散布答案。不过我确实喜欢你在这里的回答。 - Matthieu M.
6个回答

18

map元素被定义为map::value_type,它的类型是一个pair<A,B>。其中first是键,second是值。您可以编写一个函数对象来提取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 lambda函数的编译器,你可以完全消除函数对象。这对于使代码更易读且更易维护非常有用,因为你不会在代码库中留下许多小型的一次性函数对象。以下是如何使用lambda表达式更改上面的代码:

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

1
更加简洁的写法: transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const auto& val){return val.second;} ); - Wheezil

3

没有内置的函数可以使用。不过编写自己的函数也很简单:迭代遍历地图即可。迭代器会给出一个pair<A, B>,把每个second值添加到结果列表中即可。


我认为 transform 和函数对象的概念基本上是内置的。 - John Dibling
1
@John。通过说“内置”,我理解问题是要求[PHP的array_values](http://php.net/manual/en/function.array-values.php)或[Perl的`values`](http://perldoc.perl.org/functions/values.html) - 该库提供了一个完全适合任务的功能,而不仅仅是一堆你必须组合起来编写自己的“values”函数的部分。如果您的编译器或库没有提供所有最好的部分(例如select2nd或lambda),情况会更糟。 - Rob Kennedy

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

1
不幸的是,C++标准库中没有 select2nd - James McNellis
是的,我也注意到太晚了。我快要放弃那该死的SGI“文档”了。 - aschepler

2

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

请注意:std::list 很少是您实际想要使用的容器。除非您确实非常需要其特定功能。


2

好的。

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 的使用呢? - James McNellis
1
@DeadMG:因为这个问题没有标记[c++0x],所以Lambda超出了范围。 - John Dibling
@John Dibling:当MSVC10和GCC4.5都支持Lambda时,Lambda支持停止需要明确标记。 - Puppy
@DeadMG:真的吗?尽管C++和C++0x不同? - John Dibling
@DeadMG:我不同意。无论喜欢与否,许多C++程序员并没有在最流行的操作系统上使用最新和最好的编译器,因为存在遗留代码、商业决策和/或编码标准,这些标准尚不允许使用新的非官方功能以实现更高的可移植性。 - aschepler
显示剩余4条评论

0

您可以使用boost的transform_iteratorhttp://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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接