174

Is there a way to iterate over the keys, not the pairs of a C++ map?

Bogdan Balan
  • 6,493
  • 8
  • 25
  • 23
  • The idea in getting an iterator to the values is to use it in STL algorithms, for example, intersection of keys of two maps. The solution involving Boost doesn't allow this, because it will produce a Boost iterator. The worst answer gets the most votes! –  Apr 07 '18 at 11:25

20 Answers20

135

map is associative container. Hence, iterator is a pair of key,val. IF you need only keys, you can ignore the value part from the pair.

for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k =  iter->first;
//ignore value
//Value v = iter->second;
}

EDIT:: In case you want to expose only the keys to outside then you can convert the map to vector or keys and expose.

ArtemStorozhuk
  • 8,715
  • 4
  • 35
  • 53
aJ.
  • 34,624
  • 22
  • 86
  • 128
  • But then it will be really bad idea to expose the iterator of the vector outside. – Naveen Sep 18 '09 at 10:57
  • Do not expose the iterator. Just provide the keys in vector – aJ. Sep 18 '09 at 11:01
  • 5
    You may want to do this instead: `const Key& k(iter->first);` – strickli Sep 17 '13 at 16:04
  • 27
    Two things, this answers the OP's question with exactly the answer he already knew and wasn't looking for, secondly this method will not help you if you want to do something like: `std::vector v(myMap.begin(), myMap.end())`. – Andreas Magnusson Mar 05 '14 at 22:59
  • 1
    Don't convert the keys to a vector. Making a new vector defeats the purpose of iteration, which is supposed to be fast and not allocate anything. Also, it will be slow for large sets. – Kevin Chen Nov 14 '18 at 22:29
118

With C++11 the iteration syntax is simple. You still iterate over pairs, but accessing just the key is easy.

#include <iostream>
#include <map>

int main()
{
    std::map<std::string, int> myMap;

    myMap["one"] = 1;
    myMap["two"] = 2;
    myMap["three"] = 3;

    for ( const auto &myPair : myMap ) {
        std::cout << myPair.first << "\n";
    }
}
xilpex
  • 3,097
  • 2
  • 14
  • 45
John H.
  • 1,551
  • 1
  • 12
  • 18
75

If you really need to hide the value that the "real" iterator returns (for example because you want to use your key-iterator with standard algorithms, so that they operate on the keys instead of the pairs), then take a look at Boost's transform_iterator.

[Tip: when looking at Boost documentation for a new class, read the "examples" at the end first. You then have a sporting chance of figuring out what on earth the rest of it is talking about :-)]

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 2
    With boost you can write BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys){ do something } http://www.boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/adaptors/reference/map_keys.html – rodrigob Aug 29 '12 at 10:48
75

With C++17 you can use a structured binding inside a range-based for loop (adapting John H.'s answer accordingly):

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> myMap;

    myMap["one"] = 1;
    myMap["two"] = 2;
    myMap["three"] = 3;

    for ( const auto &[key, value]: myMap ) {
        std::cout << key << '\n';
    }
}

Unfortunately the C++17 standard requires you to declare the value variable, even though you're not using it (std::ignore as one would use for std::tie(..) does not work, see this discussion).

Some compilers may therefore warn you about the unused value variable! Compile-time warnings regarding unused variables are a no-go for any production code in my mind. So, this may not be applicable for certain compiler versions.

Elmar
  • 1,293
  • 10
  • 7
  • couldn't you assign it to std::ignore in principle? Would that actually hurt efficiency in the compiled code or would it actually evaluate to nothing? (I don't mean in the binding but rather as an action within the loop) – KotoroShinoto Jul 26 '19 at 13:53
  • 1
    Since C++17 you can also use [[maybe_unused]]. This suppresses the warning. Like this: `for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }` – arhuaco May 09 '20 at 07:23
  • Linters and compilers would usually take `(void) value;` as enough use to not complain, so it may be used to mark a variable unused as well. – Frax Apr 01 '21 at 17:28
58

Without Boost

You can do this by simply extending the STL iterator for that map. For example, a mapping of strings to ints:

#include <map>
typedef map<string, int> ScoreMap;
typedef ScoreMap::iterator ScoreMapIterator;

class key_iterator : public ScoreMapIterator
{
  public:
    key_iterator() : ScoreMapIterator() {};
    key_iterator(ScoreMapIterator s) : ScoreMapIterator(s) {};
    string* operator->() { return (string* const)&(ScoreMapIterator::operator->()->first); }
    string operator*() { return ScoreMapIterator::operator*().first; }
};

You could also perform this extension in a template, for a more general solution.

You use your iterator exactly like you would use a list iterator, except you're iterating over the map's begin() and end().

ScoreMap m;
m["jim"] = 1000;
m["sally"] = 2000;

for (key_iterator s = m.begin(); s != m.end(); ++s)
    printf("\n key %s", s->c_str());
Community
  • 1
  • 1
Ian
  • 11,280
  • 3
  • 36
  • 58
21

Below the more general templated solution to which Ian referred...

#include <map>

template<typename Key, typename Value>
using Map = std::map<Key, Value>;

template<typename Key, typename Value>
using MapIterator = typename Map<Key, Value>::iterator;

template<typename Key, typename Value>
class MapKeyIterator : public MapIterator<Key, Value> {

public:

    MapKeyIterator ( ) : MapIterator<Key, Value> ( ) { };
    MapKeyIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };

    Key *operator -> ( ) { return ( Key * const ) &( MapIterator<Key, Value>::operator -> ( )->first ); }
    Key operator * ( ) { return MapIterator<Key, Value>::operator * ( ).first; }
};

template<typename Key, typename Value>
class MapValueIterator : public MapIterator<Key, Value> {

public:

    MapValueIterator ( ) : MapIterator<Key, Value> ( ) { };
    MapValueIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };

    Value *operator -> ( ) { return ( Value * const ) &( MapIterator<Key, Value>::operator -> ( )->second ); }
    Value operator * ( ) { return MapIterator<Key, Value>::operator * ( ).second; }
};

All credits go to Ian... Thanks Ian.

degski
  • 642
  • 5
  • 13
14

You are looking for map_keys, with it you can write things like

BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
{
  // do something with key
}
rodrigob
  • 2,891
  • 3
  • 30
  • 34
11

Without BOOST, directly using key and value

for(auto const& [key, value]: m_map)
    {
    std::cout<<" key="<<key;
    std::cout<<" value="<<value<<std::endl;
    }
Jindřich
  • 111
  • 1
  • 2
11

With C++20, we have access to the ranges library, which has a nifty solution for this: std::views::keys

#include <ranges>

//...

std::map<int, int> myMap = {{1,2},{3,4},{5,6}};
auto keys = std::views::keys(myMap);
for(auto key : keys) {
    std::cout << key << std::endl;
}

Try it out yourself: https://godbolt.org/z/heeWv4Gh6

iamkroot
  • 171
  • 2
  • 11
6

You want to do this?

std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator endIter = myMap.end();
for(; iter != endIter; ++iter)
{
   type key = iter->first;  
   .....
}
Michael Haidl
  • 5,384
  • 25
  • 43
Naveen
  • 74,600
  • 47
  • 176
  • 233
  • Yes, i know, the problem is i have a class A { public: // i'd like to expose an iterator over keys of private map here private: map<> }; – Bogdan Balan Sep 18 '09 at 10:50
  • 1
    In that case, I think you can create a std::list by using std::trasnform and picking up only the keys from the map. Then you can expose the list iterator as inserting more elements to the list will not invalidate the existing iterators. – Naveen Sep 18 '09 at 10:58
  • @Naveen using std::transform requires iterating over the whole map and storing a copy of all the keys int he new list – Sparr Apr 02 '23 at 23:49
6

When no explicit begin and end is needed, ie for range-looping, the loop over keys (first example) or values (second example) can be obtained with

#include <boost/range/adaptors.hpp>

map<Key, Value> m;

for (auto k : boost::adaptors::keys(m))
  cout << k << endl;

for (auto v : boost::adaptors::values(m))
  cout << v << endl;
Darko Veberic
  • 451
  • 1
  • 6
  • 12
5

Here's an example of how to do it using Boost's transform_iterator

#include <iostream>
#include <map>
#include <iterator>
#include "boost/iterator/transform_iterator.hpp"

using std::map;
typedef std::string Key;
typedef std::string Val;

map<Key,Val>::key_type get_key(map<Key,Val>::value_type aPair) {
  return aPair.first;
}

typedef map<Key,Val>::key_type (*get_key_t)(map<Key,Val>::value_type);
typedef map<Key,Val>::iterator map_iterator;
typedef boost::transform_iterator<get_key_t, map_iterator> mapkey_iterator;

int main() {
  map<Key,Val> m;
  m["a"]="A";
  m["b"]="B";
  m["c"]="C";

  // iterate over the map's (key,val) pairs as usual
  for(map_iterator i = m.begin(); i != m.end(); i++) {
    std::cout << i->first << " " << i->second << std::endl;
  }

  // iterate over the keys using the transformed iterators
  mapkey_iterator keybegin(m.begin(), get_key);
  mapkey_iterator keyend(m.end(), get_key);
  for(mapkey_iterator i = keybegin; i != keyend; i++) {
    std::cout << *i << std::endl;
  }
}
algal
  • 27,584
  • 13
  • 78
  • 80
3

If you need an iterator that just returns the keys you need to wrap map's iterator in your own class that provides the desired interface. You can declare a new iterator class from scratch like here, of use existing helper constructs. This answer shows how to use Boost's transform_iterator to wrap the iterator in one that only returns the values/keys.

Community
  • 1
  • 1
sth
  • 222,467
  • 53
  • 283
  • 367
3

This answer is like rodrigob's except without the BOOST_FOREACH. You can use c++'s range based for instead.

#include <map>
#include <boost/range/adaptor/map.hpp>
#include <iostream>

template <typename K, typename V>
void printKeys(std::map<K,V> map){
     for(auto key : map | boost::adaptors::map_keys){
          std::cout << key << std::endl;
     }
}
2

You could

  • create a custom iterator class, aggregating the std::map<K,V>::iterator
  • use std::transform of your map.begin() to map.end() with a boost::bind( &pair::second, _1 ) functor
  • just ignore the ->second member while iterating with a for loop.
xtofl
  • 40,723
  • 12
  • 105
  • 192
0

Without Boost, you could do it like this. It would be nice if you could write a cast operator instead of getKeyIterator(), but I can't get it to compile.

#include <map>
#include <unordered_map>


template<typename K, typename V>
class key_iterator: public std::unordered_map<K,V>::iterator {

public:

    const K &operator*() const {
        return std::unordered_map<K,V>::iterator::operator*().first;
    }

    const K *operator->() const {
        return &(**this);
    }
};

template<typename K,typename V>
key_iterator<K,V> getKeyIterator(typename std::unordered_map<K,V>::iterator &it) {
    return *static_cast<key_iterator<K,V> *>(&it);
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::unordered_map<std::string, std::string> myMap;
    myMap["one"]="A";
    myMap["two"]="B";
    myMap["three"]="C";
    key_iterator<std::string, std::string> &it=getKeyIterator<std::string,std::string>(myMap.begin());
    for (; it!=myMap.end(); ++it) {
        printf("%s\n",it->c_str());
    }
}
0

For posterity, and since I was trying to find a way to create a range, an alternative is to use boost::adaptors::transform

Here's a small example:

#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <map>

int main(int argc, const char* argv[])
{
  std::map<int, int> m;
  m[0]  = 1;
  m[2]  = 3;
  m[42] = 0;

  auto key_range =
    boost::adaptors::transform(
      m,
      [](std::map<int, int>::value_type const& t) 
      { return t.first; }
    ); 
  for (auto&& key : key_range)
    std::cout << key << ' ';
  std::cout << '\n';
  return 0;
}

If you want to iterate over the values, use t.second in the lambda.

ipapadop
  • 1,432
  • 14
  • 19
0

Lots of good answers here, below is an approach using a couple of them which lets you write this:

void main()
{
    std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
    for (auto key : MapKeys(m))
        std::cout << key << std::endl;
}

If that's what you always wanted, then here is the code for MapKeys():

template <class MapType>
class MapKeyIterator {
public:
    class iterator {
    public:
        iterator(typename MapType::iterator it) : it(it) {}
        iterator operator++() { return ++it; }
        bool operator!=(const iterator & other) { return it != other.it; }
        typename MapType::key_type operator*() const { return it->first; }  // Return key part of map
    private:
        typename MapType::iterator it;
    };
private:
    MapType& map;
public:
    MapKeyIterator(MapType& m) : map(m) {}
    iterator begin() { return iterator(map.begin()); }
    iterator end() { return iterator(map.end()); }
};
template <class MapType>
MapKeyIterator<MapType> MapKeys(MapType& m)
{
    return MapKeyIterator<MapType>(m);
}
Superfly Jon
  • 911
  • 9
  • 7
0

I've adopted Ian's answer to work with all map types and fixed returning a reference for operator*

template<typename T>
class MapKeyIterator : public T
{
public:
    MapKeyIterator() : T() {}
    MapKeyIterator(T iter) : T(iter) {}
    auto* operator->()
    {
        return &(T::operator->()->first);
    }
    auto& operator*()
    {
        return T::operator*().first;
    }
};
-1

I know this doesn't answer your question, but one option you may want to look at is just having two vectors with the same index being "linked" information..

So in..

std::vector<std::string> vName;

std::vector<int> vNameCount;

if you want the count of names by name you just do your quick for loop over vName.size(), and when ya find it that is the index for vNameCount that you are looking for.

Sure this may not give ya all the functionality of the map, and depending may or may not be better, but it might be easier if ya don't know the keys, and shouldn't add too much processing.

Just remember when you add/delete from one you have to do it from the other or things will get crazy heh :P

bluish
  • 26,356
  • 27
  • 122
  • 180