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
2010-03-18 01:09:16
Why not make that `std::set<T> keys` to really match the functionality?
R Samuel Klatchko
2010-03-18 01:21:53
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
2010-03-18 01:47:35
+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
2010-03-18 02:12:14
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
2010-03-18 02:59:39
+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
2010-03-18 03:06:08
@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
2010-03-18 04:14:11
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
2010-03-18 03:31:02