16

Is there an stl way to get a list of values from a map?

i.e, I have:

std::map<A,B> myMap;

and I would like a function that will return just the list of values, i.e, std::list<B> (or set for that matter. Is there a built-in stl way to do this?

Amir Rachum
  • 76,817
  • 74
  • 166
  • 248
  • 1
    Duplicate of [Copy map values to vector in STL](http://stackoverflow.com/questions/771453/copy-map-values-to-vector-in-stl) – James McNellis Nov 16 '10 at 15:20
  • Not agreeing with the dup because IMO the accepted answer there is not what I would consider to be the best design choice. – John Dibling Nov 16 '10 at 15:52
  • 1
    Actually, this does not change the fact of this question being a duplicate, it merely means that you should answer the first question rather than the second to avoid scattering answers all over SO. I do like your answer here though. – Matthieu M. Nov 16 '10 at 16:02

6 Answers6

18

A map element is defined as a map::value_type, and the type of it is a pair<A,B>. first is the key and second is the value. You can write a functor to extract second from a value_type, and copy that in to a vector (or a list, or whatever you want.) The best way to do the copying is to use transform, which does just what its name implies: it takes a value of one type and transforms it to a different type of value.

Here's a complete working example:

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

EDIT:

If you have a compiler that supports C++0x lambdas, you can eliminate the functor entirely. This is very useful for making code more readable and, arguable, easier to maintain since you don't end up with dozens of little one-off functors floating around in your codebase. Here's how you would change the code above to use a lambda:

transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const MyMap::value_type& val){return val.second;} );
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 1
    Even more succinct: transform(my_map.begin(), my_map.end(), back_inserter(my_vals), [](const auto& val){return val.second;} ); – Wheezil Mar 22 '21 at 23:29
3

There's nothing built in, no. It's simple enough to write your own function, though: Iterate over the map. The iterator will give you a pair<A, B>. Add each second value to the result list.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • I would consider `transform` and the idea of functors to be more or less built-in – John Dibling Nov 16 '10 at 15:28
  • 1
    @John. by saying "built in," I interpret the question to request [PHP's `array_values`](http://php.net/manual/en/function.array-values.php) or [Perl's `values`](http://perldoc.perl.org/functions/values.html) — the library provides a function tailored exactly to the task, rather than just a bunch of pieces that you have to put together to write a `values` function of your own. Things are worse if your compiler or library doesn't provide all the best pieces (like `select2nd` or lambdas). – Rob Kennedy Nov 16 '10 at 15:46
3

You can't just "get" such a list because there is no pre-existing list stored anywhere in the guts, but you can build one:

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

Or if you really like the more STL way:

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());
aschepler
  • 70,891
  • 9
  • 107
  • 161
2

One of many "built-in" ways is of course the most obvious one. Just iterate over all pair elements, which are ordered by key (pair::first), and add the value (pair::second) to a new container, which you can construct with the correct capacity to get rid of excess allocations during the iteration and adding.

Just a note: std::list is seldom the container you actually want to be using. Unless, of course, you really, really do need its specific features.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
2

Sure.

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

If you don't have a C++0x compiler, first you have my sympathies, and second, you will need to build a quick function object for this purpose.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Where's the `std::transform` love? – James McNellis Nov 16 '10 at 15:21
  • 1
    @DeadMG: lambdas are beyond the domain because this question isn't tagged [c++0x] – John Dibling Nov 16 '10 at 15:30
  • @DeadMG: Nonetheless, I will ammend my answer to include a lamda example – John Dibling Nov 16 '10 at 15:31
  • @John Dibling: Lambda support stopped having to be explicitly tagged when MSVC10 and GCC4.5 both support them. – Puppy Nov 16 '10 at 15:34
  • @DeadMG: Really? Even though C++ and C++0x are not the same? – John Dibling Nov 16 '10 at 15:37
  • @DeadMG: I disagree. Like it or not, many C++ programmers are not already using the latest and best compilers on the most popular OSes, because of legacy code, business decisions, and/or coding standards that do not yet allow using the new unofficial features for more portability. – aschepler Nov 16 '10 at 15:39
  • @John: Yes. I expect that plenty of people are using a lambda-supporting compiler. @aschepler: That's true. But I'm hardly using some feature or library that's totally untranslatable to C++03, like perfect forwarding. It's just slightly less convenient. It's not like you can't make this answer work in C++03 without a ton of effort. – Puppy Nov 16 '10 at 15:45
  • @DeadMG: Because at the end of the day, since C++0x isn't even official yet, C++ and C++0x are *different languages*, and by your logic one might conclude that is a question is tagged only [c] it is reasonable to answer with C++ constructs. Maybe a bit extreme, but I trust you get my point. – John Dibling Nov 16 '10 at 16:02
  • @John: No, I don't get your point. C++ and C are radically different languages. C++03 and C++0x are not radically different languages- they're exceedingly similar and especially in the area which my answer concerned, it is trivial to convert to C++03. Should we ask people whether they mean C++ or C when they ask if `++a++a++=a` is undefined? – Puppy Nov 16 '10 at 16:26
0

You can use boost's 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;
}
Ben
  • 9,184
  • 1
  • 43
  • 56