369

This is one of the possible ways I come out:

struct RetrieveKey
{
    template <typename T>
    typename T::first_type operator()(T keyValuePair) const
    {
        return keyValuePair.first;
    }
};

map<int, int> m;
vector<int> keys;

// Retrieve all keys
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey());

// Dump all keys
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n"));

Of course, we can also retrieve all values from the map by defining another functor RetrieveValues.

Is there any other way to achieve this easily? (I'm always wondering why std::map does not include a member function for us to do so.)

1
  • 6
    The only think I would add t this is keys.reserve(m.size());. Commented Apr 4, 2018 at 18:07

25 Answers 25

241

While your solution should work, it can be difficult to read depending on the skill level of your fellow programmers. Additionally, it moves functionality away from the call site. Which can make maintenance a little more difficult.

I'm not sure if your goal is to get the keys into a vector or print them to cout so I'm doing both. You may try something like this:

std::map<int, int> m;
std::vector<int> key, value;
for(std::map<int,int>::iterator it = m.begin(); it != m.end(); ++it) {
  key.push_back(it->first);
  value.push_back(it->second);
  std::cout << "Key: " << it->first << std::endl;
  std::cout << "Value: " << it->second << std::endl;
}

Or even simpler, if you are using the Boost library:

map<int,int> m;
pair<int,int> me; // what a map<int, int> is made of
vector<int> v;
BOOST_FOREACH(me, m) {
  v.push_back(me.first);
  cout << me.first << "\n";
}

Personally, I like the BOOST_FOREACH version because there is less typing and it is very explicit about what it is doing.

Sign up to request clarification or add additional context in comments.

13 Comments

Go figures I'd end up back here after my Google search. Yours is the answer I prefer :)
@Jere - Have you actually worked with BOOST_FOREACH? The code you propose here is totally wrong
@Jamie - that is another way, but the boost docs show specifying the variable and its type prior to the BOOST_FOREACH if the type contains a comma. They also show typedefing it. So, I'm confused, what is wrong with my code?
Curious, wouldn't it make sense to presize the vector to prevent resize allocation?
Don't forget to do v.reserve(m.size()) to avoid having the vector resize during the transfer.
|
218
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
    vints.push_back(imap.first);

10 Comments

Nice. Forget about it = ...begin(); it != ...end. Nicest would of course be std::map having a method keys() returning that vector...
@BenHymers: It seems to me this answer was given at answered Mar 13 '12 at 22:33, which is several months after C++11 became C++.
for (auto &imap) is more precise because no copy operation.
@StudentT, better yet, for(auto const & imap : mapints).
I prefer for (auto&& imap : mapints). See edmundv.home.xs4all.nl/blog/2014/01/28/…
|
115

Yet Another Way using C++20

The ranges library has a keys view, which retrieves the first element in a pair/tuple-like type:

#include <ranges>

auto ks = std::views::keys(m);
std::vector<int> keys{ ks.begin(), ks.end() };

Two related views worth mentioning:

  1. values - to get the values in a map (2nd element in a pair/tuple-like type)
  2. elements - to get the nth elements in a tuple-like type

3 Comments

This is now the best solution
This is an incredibly easy to implement. C++ has come a long way!
You can use assign_range, too.
68

There is a boost range adaptor for this purpose:

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
vector<int> keys;
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

There is a similar map_values range adaptor for extracting the values.

5 Comments

Unfortunately, it seems like boost::adaptors is not available until Boost 1.43. The current stable release of Debian (Squeeze) only offers Boost 1.42
That's a pity. Boost 1.42 was released in Feb 2010, over 2.5 years before Squeeze.
At this point, shouldn't Squeeze Updates and or the backports repo be offering Boost 1.44?
which boost header is that defined in?
See the linked doco, it's defined in boost/range/adaptor/map.hpp
67

C++0x has given us a further, excellent solution:

std::vector<int> keys;

std::transform(
    m_Inputs.begin(),
    m_Inputs.end(),
    std::back_inserter(keys),
    [](const std::map<int,int>::value_type &pair){return pair.first;});

7 Comments

In my view there is nothing excellent about it. std::vector<int> keys; keys.reserve(m_Inputs.size()); for ( auto keyValue : m_Inputs){ keys.push_back(keyValue.first); } Is far better than the cryptic transform. Even in terms of performance. This one is better.
You can reserve the size of keys here too if you want comparable performance. use the transform if you want to avoid a for loop.
just want to add - can use [](const auto& pair)
@ivan.ukr what compiler are you using? This syntax is not allowed here: 'const auto &': a parameter cannot have a type that contains 'auto'
@ivan.ukr auto parameter in lambda is c++14
|
45

Based on @rusty-parks solution, but in c++17:

std::map<int, int> items;
std::vector<int> itemKeys;

for (const auto& [key, _] : items) {
    itemKeys.push_back(key);
}

Comments

23

@DanDan's answer, using C++11 is:

using namespace std;
vector<int> keys;

transform(begin(map_in), end(map_in), back_inserter(keys), 
            [](decltype(map_in)::value_type const& pair) {
    return pair.first;
}); 

and using C++14 (as noted by @ivan.ukr) we can replace decltype(map_in)::value_type with auto.

6 Comments

You could add keys.reserve(map_in.size()); for efficiency.
I find the transform method actually takes more code than for-loop.
const can be put behind the type! I almost forget that.
@user1633272> yes, but that's not how you measure if it is good. a, b, c take less code than author, book, customer, yet no seasoned developer would tell you to prefer them. Production code is not code golf ;)
I would never call a variable pair, especially after using namespace std (in this case I use the name entry)
|
15

Your solution is fine but you can use an iterator to do it:

std::map<int, int> m;
m.insert(std::pair<int, int>(3, 4));
m.insert(std::pair<int, int>(5, 6));
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++)
{
    int key = it->first;
    int value = it->second;
    //Do something
}

Comments

12

The SGI STL has an extension called select1st. Too bad it's not in standard STL!

Comments

11

With the structured binding (“destructuring”) declaration syntax of C++17,

you can do this, which is easier to understand.

// To get the keys
std::map<int, double> map;
std::vector<int> keys;
keys.reserve(map.size());
for(const auto& [key, value] : map) {
    keys.push_back(key);
}
// To get the values
std::map<int, double> map;
std::vector<double> values;
values.reserve(map.size());
for(const auto& [key, value] : map) {
    values.push_back(value);
}

Comments

10

Using ranges in C++20 you can use std::ranges::copy like this

#include <ranges>
std::map<int,int> mapints;
std::vector<int> vints;

std::ranges::copy(mapints | std::views::keys, std::back_inserter(vints));

if you want values instead of keys

std::ranges::copy(mapints | std::views::values, std::back_inserter(vints));

and if you don't like the pipe syntax

std::ranges::copy(std::views::values(mapints), std::back_inserter(vints));

Comments

8

I think the BOOST_FOREACH presented above is nice and clean, however, there is another option using BOOST as well.

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

std::map<int, int> m;
std::vector<int> keys;

using namespace boost::lambda;

transform(      m.begin(), 
                m.end(), 
                back_inserter(keys), 
                bind( &std::map<int,int>::value_type::first, _1 ) 
          );

copy( keys.begin(), keys.end(), std::ostream_iterator<int>(std::cout, "\n") );

Personally, I don't think this approach is as clean as the BOOST_FOREACH approach in this case, but boost::lambda can be really clean in other cases.

Comments

8

Bit of a c++11 take:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto & kvp : items)
{
    itemKeys.emplace_back(kvp.first);
    std::cout << kvp.first << std::endl;
}

Comments

8

Here's a nice function template using C++11 magic, working for both std::map, std::unordered_map:

template<template <typename...> class MAP, class KEY, class VALUE>
std::vector<KEY>
keys(const MAP<KEY, VALUE>& map)
{
    std::vector<KEY> result;
    result.reserve(map.size());
    for(const auto& it : map){
        result.emplace_back(it.first);
    }
    return result;
}

Check it out here: http://ideone.com/lYBzpL

1 Comment

This is the best only because it's the only solution reserving the size first
7

Also, if you have Boost, use transform_iterator to avoid making a temporary copy of the keys.

Comments

6

You can use the versatile boost::transform_iterator. The transform_iterator allows you to transform the iterated values, for example in our case when you want to deal only with the keys, not the values. See http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

Comments

6

The cleanest solution yet, using C++23:

#include <format>  // std::vector formatting 
#include <print>   // std::println
#include <ranges>  // std::views

map<int, int> m = /* ... */;

vector<int> keys(from_range, views::keys(m));
vector<int> values(from_range, views::values(m));

println("{}", keys);

See live example at Compiler Explorer.

This uses the new std::vector constructor taking std::from_range_t, as well as std::views::keys and std::views::values from <ranges>.

Comments

4

The best non-sgi, non-boost STL solution is to extend map::iterator like so:

template<class map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<class map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<class map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

and then use them like so:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        vector<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(keys.begin(), kb, ke);

//      // method two
//      keys.insert(keys.begin(),
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(keys.begin(), key_begin(test), key_end(test));

        string one = keys[0];

1 Comment

I'll leave it to the reader to also create the const_iterator and reverse iterators if/when needed.
4

I found the following three lines of code as the easiest way:

// save keys in vector

vector<string> keys;
for (auto & it : m) {
    keys.push_back(it.first);
}

It is a shorten version of the first way of this answer.

Comments

3

With Eric Niebler's range-v3 library, you can take a range and write it out directly to a container using ranges::to (hopefully soon in std, maybe C++26?):

[Demo]

#include <fmt/ranges.h>
#include <map>
#include <range/v3/all.hpp>

int main() {
    std::map<int, int> m{ {1, 100}, {2, 200}, {3, 300} };
    auto keys{ m | ranges::views::keys | ranges::to<std::vector<int>>() };
    fmt::print("{}", keys);
}

// Outputs: [1, 2, 3]

1 Comment

std::ranges::to is already in C++23
1

The following functor retrieves the key set of a map:

#include <vector>
#include <iterator>
#include <algorithm>

template <class _Map>
std::vector<typename _Map::key_type> keyset(const _Map& map)
{
    std::vector<typename _Map::key_type> result;
    result.reserve(map.size());
    std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
        return kvpair.first;
    });
    return result;
}

Bonus: The following functors retrieve the value set of a map:

#include <vector>
#include <iterator>
#include <algorithm>
#include <functional>

template <class _Map>
std::vector<typename _Map::mapped_type> valueset(const _Map& map)
{
    std::vector<typename _Map::mapped_type> result;
    result.reserve(map.size());
    std::transform(map.cbegin(), map.cend(), std::back_inserter(result), [](typename _Map::const_reference kvpair) {
        return kvpair.second;
    });
    return result;
}

template <class _Map>
std::vector<std::reference_wrapper<typename _Map::mapped_type>> valueset(_Map& map)
{
    std::vector<std::reference_wrapper<typename _Map::mapped_type>> result;
    result.reserve(map.size());
    std::transform(map.begin(), map.end(), std::back_inserter(result), [](typename _Map::reference kvpair) {
        return std::ref(kvpair.second);
    });
    return result;
}

Usage:

int main()
{
    std::map<int, double> map{
        {1, 9.0},
        {2, 9.9},
        {3, 9.99},
        {4, 9.999},
    };
    auto ks = keyset(map);
    auto vs = valueset(map);
    for (auto& k : ks) std::cout << k << '\n';
    std::cout << "------------------\n";
    for (auto& v : vs) std::cout << v << '\n';
    for (auto& v : vs) v += 100.0;
    std::cout << "------------------\n";
    for (auto& v : vs) std::cout << v << '\n';
    std::cout << "------------------\n";
    for (auto& [k, v] : map) std::cout << v << '\n';

    return 0;
}

Expected output:

1
2
3
4
------------------
9
9.9
9.99
9.999
------------------
109
109.9
109.99
109.999
------------------
109
109.9
109.99
109.999

1 Comment

A real bonus would be to have such a function where the output container is a type T instead of a forced vector.
0

With atomic map example

#include <iostream>
#include <map>
#include <vector> 
#include <atomic>

using namespace std;

typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;

int main()
{
    atomic_map_t m;

    m[4] = 456;
    m[2] = 45678;

    vector<int> v;
    for(map<int,atomic_uint32_t>::iterator it = m.begin(); it != m.end(); ++it) {
      v.push_back(it->second);
      cout << it->first << " "<<it->second<<"\n";
    }

    return 0;
}

Comments

0

You can use get_map_keys() from fplus library:

#include<fplus/maps.hpp>
// ...

int main() {
    map<string, int32_t> myMap{{"a", 1}, {"b", 2}};
    vector<string> keys = fplus::get_map_keys(myMap);
    // ...
    return 0;
}

Comments

-2

Slightly similar to one of examples here, simplified from std::map usage perspective.

template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
    std::vector<KEY> keys(map.size());
    for (const auto& it : map)
        keys.push_back(it.first);
    return keys;
}

Use like this:

auto keys = getKeys(yourMap);

1 Comment

Hey, I know this answer is old but it's also wrong. Initializing with size map.size() means double the vector size return. Please fix to save someone else the headache :(
-4

(I'm always wondering why std::map does not include a member function for us to do so.)

Because it can't do it any better than you can do it. If a method's implementation will be no superior to a free function's implementation then in general you should not write a method; you should write a free function.

It's also not immediately clear why it's useful anyway.

6 Comments

There are reasons other than efficiency for a library to provide a method, such as "batteries included" functionality, and a coherent, encapsulated API. Although admittedly neither of those terms describe the STL particularly well :) Re. not clear why it's useful -- really? I think it's pretty obvious why listing the available keys is a useful thing to be able to do with a map/dict: it depends on what you're using it for.
By this reasoning, we should't have empty() because it can be implemented as size() == 0.
What @gd1 said. While there shouldn't be a lot of functional redundancy in a class, insisting on absolutely zero is not a good idea IMO - at least until C++ allows us to "bless" free functions into methods.
In older versions of C++ there were containers for which empty() and size() could reasonably have different performance guarantees, and I think the spec was sufficiently loose as to permit this (specifically, linked lists that offered constant-time splice()). As such, decoupling them made sense. I don't think this discrepancy is permitted any more, however.
I agree. C++ treats std::map<T,U> as a container of pairs. In Python, a dict acts like its keys when iterated over, but lets you say d.items() to get the C++ behavior. Python also provides d.values(). std::map<T,U> certainly could provide a keys() and values() method that return an object that has begin() and end() that provide iterators over the keys and values.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.