2011-10-05 7 views
10
map<T,Z> m= ...; 
vector<T> v; 
v.reserve(m.size); 
for(map<T,Z>::iterator it=m.begin();it!=m.end();++it) 
{ 
v.push_back(it->first); 
} 

Esiste un posto più bello versione 1-line utilizzando qualche funzione STL (s)?Usa STL per popolare un vettore <T> dalla mappa <T,Z> 's chiavi

modifica: non utilizzando C++ 11!

+2

Overkill? Non stai digitando troppe righe di codice per ottenerlo comunque. –

+0

@Als: da quella logica molto di std :: l'algoritmo è ugualmente inutile. –

+0

@Mahesh - Stavo pensando a qualche magia di back-inserter, o qualcosa del genere –

risposta

8

Portatile:

struct SelectKey { 
    template <typename F, typename S> 
    F operator()(const std::pair<const F, S> &x) const { return x.first; } 
}; 

std::transform(m.cbegin(), m.cend(), std::back_inserter(v), SelectKey()); 

penso che alcune implementazioni di STL hanno estensione non-standard chiamato select1st, che è l'equivalente di SelectKey mostrato qui. Come ha sottolineato K-Ballo nei commenti, c'è anche una versione TR1. Mi piace la versione con il nome esplicito in quanto è più facile vedere cosa sta succedendo.

Dal momento che non c'è bisogno di stato, è possibile cavarsela con un po 'meno boilerplate utilizzando una funzione reale, piuttosto che un funtore:

template <typename F, typename S> 
F SelectKey()(const std::pair<const F, S> &x) { return x.first; } 

std::transform(m.cbegin(), m.cend(), std::back_inserter(v), SelectKey); 

Se si potesse utilizzare C++ 11, è possibile utilizzare un lambda, che mantiene il codice di scelta vicino a dove viene utilizzato:

std::transform(m.cbegin(), m.cend(), std::back_inserter(v), 
       [](const std::pair<const F, S> &x) { return x.first; }); 

o addirittura gamma-based per-loop, che è probabilmente la più elegante e leggibile:

for(const auto &x : m) { 
    v.push_back(x.first); 
} 
+1

Che ne dici di lambda? sarà davvero one-liner allora. – Dani

+2

L'operatore '()' dovrebbe essere 'const'. In TR1 ciò potrebbe essere ottenuto con 'std :: bind (& map < T, Z > :: value_type :: first, _1)'. –

+3

Non dovrebbe templatizzare la struct, ma piuttosto 'operator()'. E se si vuole veramente templatizzare la struttura, è necessario fornire argomenti durante la sua creazione: 'SelectKey ()'. – Xeo

2

In C++ 11, è possibile utilizzare le espressioni lambda:

typedef std::map< std::string, std::string > map_t; 
map_t map; 
std::vector<std::string> v; 

std::for_each(map.begin(), map.end(), [&v](map_t::value_type const& it) 
     { 
      v.push_back(it.first); 
     }); 
4

Pre C++ 11, è possibile utilizzare trasformare e una funzione personalizzata struct:

template <class K, class V> 
struct key_selector : std::unary_function<const std::pair<K, V>&, const K&> 
{ 
    const K& operator()(const std::pair<K, V>& element) const 
    { 
     return element.first; 
    } 
}; 

transform(m.begin(), m.end(), back_inserter(v), key_selector<T,Z>()); 

Se si dispone di l'accesso per aumentare o TR1, è possibile sostituire con key_selectormem_fn

transform(m.begin(), m.end(), back_inserter(v), mem_fn(&map<T,Z>::value_type::first)); 

Post- C++ 11, è possibile utilizzare lambda:

transform(m.begin(), m.end(), back_inserter(v), [](const map<T,Z>::value_type& x) {return x.first;}); 
+0

In pre C++ 11 non si poteva usare 'bind'. Se fai affidamento su estensioni o boost non standard, dillo. –

+0

@Christian: è vero, con l'avvertenza che pre-C++ 11 si potrebbe usare 'std :: tr1 :: bind'. – ildjarn

+0

@ildjarn Sì, ma poi dovrebbe dirlo e non pretendere che sia lo standard. –

1

Si può fare qualcosa sulla falsariga di:

std::transform(m.begin(), m.end(), std::back_inserter(v), FUNCTOR); 

Dove functor dipende da quale versione del STL o di librerie e compilatori che avete.

C++ 11 (lambda)

std::transform(m.begin(), m.end(), std::back_inserter(v), [](map<T,Z>::const_reference a) { return a.first; }); 

C++ 11 (std :: get)

std::transform(m.begin(), m.end(), std::back_inserter(v), &std::get<0>); 

C++ SGI STL ha una chiamata select1st functor utilizzabile

std::transform(m.begin(), m.end(), std::back_inserter(v), select1st); 

C++ 03 (Non C++ 11) che utilizza un oggetto funtore come altre persone hanno descritto.

Problemi correlati