Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate keys in a C++ map

Tags:

c++

stl

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.


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

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 :-)]


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.


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

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.


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
}