2010-03-18 14 views
10

¿Existe un JavaSet keySet() equivalente para C++ 's std::map?¿Existe un JavaSet keySet() equivalente para C++ 's std :: map?

El método Java keySet() devuelve "a set view of the keys contained in this map."

+1

Es siempre me confundió por qué no hay función miembro para esto en 'std :: map'. Sé que es fácil de implementar usted mismo, pero también hay muchas cosas que sí lo hicieron en el STL.Me gustaría saber si alguien sabe cuál fue el motivo para * no * incluirlo. –

+6

@Tyler: no está allí porque el mapa es un contenedor. Su único propósito es proporcionar una estructura de contenedor asociativo y acceso a sus contenidos. No es responsable de proporcionar todas las pequeñas cosas pequeñas que uno podría hacer con él como contenedor, esa es la tarea para los algoritmos y el código definido por el usuario y no el contenedor. –

+0

@darid Eso tiene sentido, pero de nuevo hay 'std :: string' –

Respuesta

1

Tal vez el siguiente podría ser de utilidad:

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

Una sobrecarga para el make_key_set función que toma secuencias compatibles con STL UCH como std :: vector, std :: deque o std :: lista puede ser de la siguiente manera:

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); 
    } 
} 
+2

mi único problema es que esta es una funcionalidad que se requiere con frecuencia, al no hacerlo de facto como parte de STL. Solo se fomentan los errores de codificación. –

+0

Esto es O (n * log (n)). Puede hacerlo O (n) reemplazando 'set.insert ((itr ++) -> first);' con 'set.insert (set.end(), (itr ++) -> first);' – Notinlist

0

se pueden implementar por sí mismo:

vector<T> keys; 
for (map<T,S>::iterator it=m.begin(); it!=m.end; it++) 
    keys.push_back(it->first) 
+2

¿Por qué no hacer que 'std :: set keys' realmente coincida con la funcionalidad? –

+1

teclas() debe ser útil. pero recuerda que tenemos las llaves porque queremos procesarlas más tarde. p.ej. es posible que necesitemos iterar sobre las claves, entonces ¿por qué no iterar directamente sobre el mapa? –

1

Aquí es un revestimiento de uno y un bit:

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

Si Don 't tiene 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; } 
}; 
+0

Gracias, eso es mucho más fácil (más corto) que las otras sugerencias ... –

8

Todas las respuestas presentadas hasta ahora terminan creando un std::set directamente, lo que puede no ser ideal: si sólo desea ser capaz de iterar sobre las claves, que no quieren tener la sobrecarga de crear un todo contenedor nuevo.

Una opción más flexible sería utilizar un iterador de transformación que convierta un iterador std::map en algún tipo de iterador que solo arroje la clave cuando se desreferencia. Esto es bastante sencillo mediante el impulso de transformación iterador:

#include <functional> 
#include <boost/iterator/transform_iterator.hpp> 

// You may already have a select1st implementation; if not, you should :-) 
template <typename Pair> 
struct select1st 
    : std::unary_function<const Pair&, const typename Pair::first_type&> 
{ 
    const typename Pair::first_type& operator()(const Pair& p) const 
    { 
     return p.first; 
    } 
}; 

template <typename C> 
boost::transform_iterator< 
    select1st<typename C::value_type>, typename C::const_iterator 
> begin_keys(const C& c) 
{ 
    return boost::make_transform_iterator(
     c.begin(), select1st<typename C::value_type>() 
    ); 
} 

template <typename C> 
boost::transform_iterator< 
    select1st<typename C::value_type>, typename C::const_iterator 
> end_keys(const C& c) 
{ 
    return boost::make_transform_iterator(
     c.end(), select1st<typename C::value_type>() 
    ); 
} 

Con estas funciones de utilidad, puede convertir cualquier rango de std::map iteradores (iteradores o en cualquier otro par contenedor asociativo que pueda tener) en un rango de sólo las teclas . A modo de ejemplo:

salidas
#include <iostream> 
#include <iterator> 
#include <map> 

int main() 
{ 
    std::map<int, int> m; 
    m.insert(std::make_pair(1, 2)); 
    m.insert(std::make_pair(2, 4)); 
    m.insert(std::make_pair(3, 6)); 

    std::copy(
     begin_keys(m), end_keys(m), 
     std::ostream_iterator<int>(std::cout, ",")); 
} 

Este programa:

1,2,3, 

Si realmente quiere un std::set que contiene las claves, puede crear fácilmente uno usando estos iteradores:

std::set<int> s(begin_keys(m), end_keys(m)); 

general , es una solución más flexible.

Si usted no tiene Boost o no desea usar Boost o no puede usar Boost, este iterador transformar específica se puede implementar con bastante facilidad:

#include <iterator> 

template <typename C> 
class key_iterator 
    : public std::iterator< 
      std::bidirectional_iterator_tag, 
      typename C::key_type, 
      typename C::difference_type, 
      typename C::pointer, 
      typename C::reference 
     > 
{ 
public: 

    key_iterator() { } 
    explicit key_iterator(typename C::const_iterator it) : it_(it) { } 

    typename const C::key_type& operator*() const { return it_->first; } 
    typename const C::key_type* operator->() const { return &it_->first; } 

    key_iterator& operator++() { ++it_; return *this; } 
    key_iterator operator++(int) { key_iterator it(*this); ++*this; return it; } 

    key_iterator& operator--() { --it_; return *this; } 
    key_iterator operator--(int) { key_iterator it(*this); --*this; return it; } 

    friend bool operator==(const key_iterator& lhs, const key_iterator& rhs) 
    { 
     return lhs.it_ == rhs.it_; 
    } 

    friend bool operator!=(const key_iterator& lhs, const key_iterator& rhs) 
    { 
     return !(lhs == rhs); 
    } 

private: 

    typename C::const_iterator it_; 
}; 

template <typename C> 
key_iterator<C> begin_keys(const C& c) { return key_iterator<C>(c.begin()); } 

template <typename C> 
key_iterator<C> end_keys(const C& c) { return key_iterator<C>(c.end()); } 

El uso de esto es la misma en cuanto a la versión de Boost.

+1

Hehe - buena respuesta Sr. McNellis :) +1. –

+0

Cambie su SO UN a @Jon Skeet. Entonces, cuando alguien presiona al voto negativo, en realidad lo vota dos veces en su lugar :) – Matt

Cuestiones relacionadas