How to retrieve all keys (or values) from a std::map and put them into a vector?
Asked Answered
H

25

352

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.)

Hepburn answered 21/9, 2008 at 3:23 Comment(1)
The only think I would add t this is keys.reserve(m.size());.Saree
B
232

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.

Broca answered 21/9, 2008 at 4:38 Comment(13)
Go figures I'd end up back here after my Google search. Yours is the answer I prefer :)Precatory
@Jere - Have you actually worked with BOOST_FOREACH? The code you propose here is totally wrongTwannatwattle
Manuel is correct, the syntax for BOOST_FOREACH iterating over a map is to first define the element type with no commas (ie typedef pair<const int,int> element_type;) then iterate using the actual elements (ie BOOST_FOREACH(element_type& e, m)Foreordination
@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?Broca
@Twannatwattle - sorry, I just saw your response. Can you specify what is wrong?Broca
@Jere.Jones, your code is wrong because your variable is an iterator. The entire point of BOOST_FOREACH is that you don't need iterators, you just get given the elements one after the other. See my previous comment or this code dump pastie.org/1045222Foreordination
@Manual - OMG, as soon as I read your first sentence I smacked my head. Thanks! I have corrected it so as to not confuse future visitors.Broca
Curious, wouldn't it make sense to presize the vector to prevent resize allocation?Torque
Now when C++11 is available, the method described by Juan below is better in my opinion: //c++0x too std::map<int,int> mapints; std::vector<int> vints; for(auto imap: mapints) vints.push_back(imap.first);Scandal
Don't forget to do v.reserve(m.size()) to avoid having the vector resize during the transfer.Thirza
@BrianWhite This is bad advice. Stroustrup recommends to not call reserve (stroustrup.com/bs_faq2.html).Inoculate
@Étienne, his observation at that link (not actually a recommendation) is under the context of "the cost of std::vector growing incrementally". In this case, it's moving to a known, fixed size so I think it's only a benefit.Thirza
@Brian White you may also make the code slower by calling reserve explicitly, depending on the size of the map. It is a recommendation from Stroustrup to not call it explicitly because this is really a tiny speed optimization with a reasonable implementation of std::vector, he also recommend it in his c++ book "the c++ programming language".Inoculate
V
204
//c++0x too
std::map<int,int> mapints;
std::vector<int> vints;
for(auto const& imap: mapints)
    vints.push_back(imap.first);
Verrazano answered 13/3, 2012 at 22:33 Comment(10)
Nice. Forget about it = ...begin(); it != ...end. Nicest would of course be std::map having a method keys() returning that vector...Wentzel
@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++.Giaour
for (auto &imap) is more precise because no copy operation.Dwarfish
@StudentT, better yet, for(auto const & imap : mapints).Blackness
I prefer for (auto&& imap : mapints). See edmundv.home.xs4all.nl/blog/2014/01/28/…Uriniferous
Also, some of us have to work in environments without C++11 support -_-Halfmast
If you plan to use this efficiently, you should add vints.reserve( mapints.size() ); before the loop.Preconception
Person from the future here. Regarding @user283145 's comment, now C++==C++17 and that won't last too long either. I, for one, appreciate when answers specify language compatibility. "C++" is a language that has and will continue to change.Manaker
@Antonio I rolled-back your edit, calling reserve explicitely is not the recommended way and is a sign of premature optimization in most cases (see stroustrup.com/bs_faq2.html)Inoculate
@Étienne I agree it’s a matter of style, although depending on the platform I have seen allocation being more or less costly (in Android it’s more costly in my experience). I see giving any available information to the compiler to optimize as a good practice, in this case being: “the vector will grow at least this big”Marr
D
85

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 kv = std::views::keys(m);
std::vector<int> keys{ kv.begin(), kv.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
Durst answered 23/6, 2021 at 6:25 Comment(3)
This is now the best solutionBash
This is an incredibly easy to implement. C++ has come a long way!Corking
You can use assign_range, too.Englebert
U
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.

Unshod answered 5/3, 2012 at 19:29 Comment(5)
Unfortunately, it seems like boost::adaptors is not available until Boost 1.43. The current stable release of Debian (Squeeze) only offers Boost 1.42Waldron
That's a pity. Boost 1.42 was released in Feb 2010, over 2.5 years before Squeeze.Unshod
At this point, shouldn't Squeeze Updates and or the backports repo be offering Boost 1.44?Inclinable
which boost header is that defined in?Legofmutton
See the linked doco, it's defined in boost/range/adaptor/map.hppUnshod
M
66

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;});
Miyasawa answered 8/5, 2010 at 13:7 Comment(7)
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.Louth
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.Miyasawa
just want to add - can use [](const auto& pair)Disaffiliate
@Disaffiliate what compiler are you using? This syntax is not allowed here: 'const auto &': a parameter cannot have a type that contains 'auto'Ciborium
Visual C++ 2015 Update 1, also works for me with Visual C++2015 Update 2Disaffiliate
@Disaffiliate auto parameter in lambda is c++14Incarnate
@Louth How's your version more performant if this one also has a keys.reserve()? Just curious.Spangle
I
38

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);
}
Imogeneimojean answered 3/5, 2019 at 21:8 Comment(2)
I don't think std::ignore ca be used in structured bindings in this way. I'm getting a compile error. It should be sufficient to just use a regular variable e.g. ignored that simply doesn't get used.Shutter
@j-b Thanks. Indeed, std::ignore is intended for use with std::tie but not with structural bindings. I've updated my code.Imogeneimojean
G
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.

Grunter answered 16/9, 2016 at 12:47 Comment(5)
You could add keys.reserve(map_in.size()); for efficiency.Saree
I find the transform method actually takes more code than for-loop.Barthold
const can be put behind the type! I almost forget that.Blumenfeld
@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 ;)Mores
I would never call a variable pair, especially after using namespace std (in this case I use the name entry)Marr
T
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
}
Talishatalisman answered 21/9, 2008 at 3:57 Comment(0)
M
12

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

Mcfall answered 21/9, 2008 at 3:31 Comment(0)
R
10

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);
}
Religieux answered 27/1, 2021 at 12:12 Comment(0)
D
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.

Dodona answered 21/9, 2008 at 6:24 Comment(0)
E
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;
}
Effuse answered 10/3, 2016 at 1:6 Comment(0)
C
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

Cabbagehead answered 10/8, 2016 at 23:34 Comment(1)
This is the best only because it's the only solution reserving the size firstJodhpur
R
7

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

Routinize answered 21/9, 2008 at 4:5 Comment(0)
S
7

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));
Sayyid answered 20/5, 2022 at 7:16 Comment(0)
C
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

Carabao answered 21/9, 2008 at 4:14 Comment(0)
C
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];
Caecum answered 5/3, 2010 at 19:15 Comment(1)
I'll leave it to the reader to also create the const_iterator and reverse iterators if/when needed.Caecum
J
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.

Johnniejohnny answered 30/7, 2020 at 22:50 Comment(0)
Q
2

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>.

Quoth answered 4/1 at 0:13 Comment(0)
O
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
Oleander answered 20/12, 2020 at 10:8 Comment(1)
A real bonus would be to have such a function where the output container is a type T instead of a forced vector.Afterdeck
B
1

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]
Byssinosis answered 1/2, 2023 at 0:27 Comment(0)
E
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;
}
Elater answered 24/3, 2020 at 19:39 Comment(0)
B
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;
}
Bemock answered 7/6, 2021 at 10:10 Comment(0)
A
-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);
Absorptivity answered 14/4, 2019 at 14:12 Comment(1)
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 :(Agglutinogen
P
-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.

Pelagi answered 21/9, 2008 at 11:13 Comment(6)
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.Sivas
By this reasoning, we should't have empty() because it can be implemented as size() == 0.Problem
What @Problem 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.Enenstein
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.Pelagi
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.Medan
c# has such method. I think that having such function is merely about the mindset (type theory and object oriented and functionnal mindset). It seems to me that DrPizza has an imperative and performance centric point of view (which is, in the end it's always what happens after compilation, and after the front end). Ina nutshell: should we think like mathematicians (with concepts) or like mecanicians (with what/how to do) ?Amerson

© 2022 - 2024 — McMap. All rights reserved.