tags:

views:

287

answers:

4

I tried searching documentation but couldn't find an equivalent of keyset function for maps in C++ stl. Is it C++ really that poor.

A: 

you can implement it yourself:

vector<T> keys;
for (map<T,S>::iterator it=m.begin(); it!=m.end; it++)
  keys.push_back(it->first)
Yin Zhu
Why not make that `std::set<T> keys` to really match the functionality?
R Samuel Klatchko
keys() should be handy. but remember we get the keys because we want to process them later. e.g. we may need to iterate over the keys, then why not directly iterate over the map.
Yin Zhu
+4  A: 

Perhaps the following might be of use:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <map>
#include <set>
#include <string>

template< class Key, 
          class T, 
          class Comparator,
          class MapAllocator,
          class SetAllocator>
void make_key_set(const std::map<Key,T,Comparator,MapAllocator>& map, 
                  std::set<Key,Comparator,SetAllocator>& set)
{
   set.clear();
   typedef typename std::map<Key,T,Comparator,MapAllocator> map_type;
   typename map_type::const_iterator itr = map.begin();
   while (map.end() != itr)
   {
      set.insert((itr++)->first);
   }
}

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

  m["one"] = 1.1;
  m["two"] = 2.2;
  m["three"] = 3.3;

  std::set<std::string> key_set;

  make_key_set(m,key_set); 

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

  return  0;
}

An overload for the make_key_set function taking STL compatible sequences such as std::vector, std::deque or std::list can be as follows:

template< class Key, 
          class T, 
          class Comparator,
          class MapAllocator,
          class SeqAllocator,
          template<class,class> class Sequence>
void make_key_set(const std::map<Key,T,Comparator,MapAllocator>& map, 
                  Sequence<Key,SeqAllocator>& sequence)
{
   sequence.clear();
   typedef typename std::map<Key,T,Comparator,MapAllocator> map_type;
   typename map_type::const_iterator itr = map.begin();
   while (map.end() != itr)
   {
      sequence.push_back((itr++)->first);
   }
}
Beh Tou Cheh
my only issue is that this is a functionality which is frequently required, by not making it de facto part of STL. Only coding errors are encouraged.
A: 

Yet another solution:

struct key_of {
    template<typename T>
    const typename T::first_type& operator()(const T& p) const {
        return p.first;
    }
};

// ...
{
    map<int,char> m = map_list_of(0,'a')(1,'b')(2,'c'); // boost.assign
    vector<int> keys; // vector: map already guarantees unique keys
    keys.reserve(m.size()); // so no need for default ctor
    transform(m.begin(), m.end(), back_inserter(keys), key_of());
}
wilhelmtell
+1 for the solution that uses `transform`; that's what it was made for! If your functor was renamed to `first_of`, and you made the functor `template <typename T> const typename T::first_type }`, you'd have a functor that works on any pair, not strictly a "key" for a certain pair type. Lastly, I'd recommend you do `vector<int> keys; keys.reserve(m.size());` and then use `std::back_inserter(keys)` instead of `keys.begin()`. This prevents a vector full of default-constructed types (which isn't a vector requirement), while maintaining efficiency.
GMan
@GMan Thanks, I applied your patch ;) Well, most of it. I kept the name `key_of` because I personally like slightly more humane interfaces than what's common in C++. That is, I'd rather have two functions (say, `key_of()` and `first_iterator_of()`) that do the same thing but have a names that expresses clearly an intent then have a single function (say, `first_of()`) that poses no redundancy but hides intent.
wilhelmtell
Huh? Why was this downvoted? It's a perfectly cromulent answer.
spong
A: 

Here is a one-and-a-bit liner:

map<K,V> m;
...
// Useful stuff goes here
...
set<K> s;
transform(m.begin(), m.end(), inserter(s, s.begin()), select1st<pair<K,V> >());

If you don't have select1st:

template <class P>
struct select1st : public std::unary_function<P, typename P::first_type>
{
    const typename P::first_type& operator()(const P &arg) const { return arg.first; }
};
spong