2008-09-21 16 views
157

Dies ist einer der möglichen Wege, ich komme aus:Wie alle Schlüssel (oder Werte) von einer std :: map abgerufen und in einen Vektor eingefügt werden?

struct RetrieveKey 
{ 
    template <typename T> 
    typename T::first_type operator()(T keyValuePair) const 
    { 
     return keyValuePair.first; 
    } 
}; 

map<int, int> m; 
vector<int> keys; 

// Retrieve all keys 
transform(m.begin(), m.end(), back_inserter(keys), RetrieveKey()); 

// Dump all keys 
copy(keys.begin(), keys.end(), ostream_iterator<int>(cout, "\n")); 

Natürlich können wir auch alle Werte aus der Karte abrufen, indem RetrieveValues ​​eine andere Funktors definieren.

Gibt es einen anderen Weg, um dies leicht zu erreichen? (Ich frage mich immer, warum std :: map für uns keine Member-Funktion enthält.)

+3

Ihre Lösung ist die beste ... – linello

Antwort

124

Während Ihre Lösung funktionieren sollte, kann es schwierig sein, sie zu lesen, abhängig vom Können Ihrer Mitprogrammierer. Außerdem wird die Funktionalität von der Aufrufsite entfernt. Das kann die Wartung etwas erschweren.

Ich bin mir nicht sicher, ob es Ihr Ziel ist, die Schlüssel in einen Vektor zu bekommen oder sie zu drucken, damit ich beides mache. Sie können so etwas wie dies versuchen:

map<int, int> m; 
vector<int> v; 
for(map<int,int>::iterator it = m.begin(); it != m.end(); ++it) { 
    v.push_back(it->first); 
    cout << it->first << "\n"; 
} 

Oder noch einfacher, wenn Sie Boost sind:

map<int,int> m; 
pair<int,int> me; // what a map<int, int> is made of 
vector<int> v; 
BOOST_FOREACH(me, m) { 
    v.push_back(me.first); 
    cout << me.first << "\n"; 
} 

Persönlich mag ich die BOOST_FOREACH Version, weil es weniger tippen und es ist sehr deutlich, was es tut.

+1

Go Figuren Ich würde hier nach meiner Google-Suche enden. Dein ist die Antwort * ich * bevorzuge :) – mpen

+4

@Jere - Hast du tatsächlich mit 'BOOST_FOREACH' gearbeitet? Der Code, den Sie hier vorschlagen, ist völlig falsch – Manuel

+0

Manuel ist korrekt, die Syntax für BOOST_FOREACH beim Iterieren über eine Map besteht darin, zuerst den Elementtyp ohne Kommas zu definieren (dh typedef pair element_type;) und dann mit den tatsächlichen Elementen zu iterieren (zB BOOST_FOREACH (element_type & e, m) –

10

Die SGI STL hat eine Erweiterung namens select1st. Schade, es ist nicht in Standard-STL!

6

Ihre Lösung ist in Ordnung, aber Sie einen Iterator, es zu tun verwenden können:

std::map<int, int> m; 
m.insert(std::pair<int, int>(3, 4)); 
m.insert(std::pair<int, int>(5, 6)); 
for(std::map<int, int>::const_iterator it = m.begin(); it != m.end(); it++) 
{ 
    int key = it->first; 
    int value = it->second; 
    //Do something 
} 
5

Auch wenn Sie Boost-haben, verwenden transform_iterator zu vermeiden, dass eine temporäre Kopie des Schlüssels.

9

Ich denke, der BOOST_FOREACH oben ist schön und sauber, aber es gibt eine andere Option mit BOOST auch.

#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/bind.hpp> 

std::map<int, int> m; 
std::vector<int> keys; 

using namespace boost::lambda; 

transform(  m.begin(), 
       m.end(), 
       back_inserter(keys), 
       bind(&std::map<int,int>::value_type::first, _1) 
     ); 

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

Ich persönlich glaube nicht, dieser Ansatz so sauber wie der BOOST_FOREACH Ansatz ist in diesem Fall, aber boost :: Lambda kann in anderen Fällen wirklich sauber sein.

-5

(ich frage mich immer, warum std :: map keine Memberfunktion enthält für uns, dies zu tun.)

Weil es nicht besser machen können als Sie es tun können . Wenn die Implementierung einer Methode der Implementierung einer freien Funktion nicht überlegen ist, sollten Sie im Allgemeinen keine Methode schreiben; Sie sollten eine freie Funktion schreiben.

Es ist auch nicht sofort klar, warum es sowieso nützlich ist.

+6

Es gibt andere Gründe als die Effizienz einer Bibliothek, eine Methode wie die Funktionalität "Batterien eingeschlossen" und eine kohärente, gekapselte API bereitzustellen. Obwohl zugegebenermaßen keiner der Begriffe die STL besonders gut beschreibt :) Re.nicht klar, warum es nützlich ist - wirklich? Ich denke, es ist ziemlich offensichtlich, warum das Auflisten der verfügbaren Schlüssel eine nützliche Sache ist, um mit einer Karte/einem Diktat fertig zu werden: es hängt davon ab, wofür Sie es verwenden. – andybuckley

+3

Durch diese Argumentation sollten wir 'empty()' nicht haben, weil es als 'size() == 0' implementiert werden kann. – gd1

+1

Was @ gd1 sagte. Während es in einer Klasse nicht viel funktionale Redundanz geben sollte, ist es absolut keine gute Idee, auf Null zu bestehen - zumindest bis C++ uns erlaubt, freie Funktionen in Methoden zu "segnen". – einpoklum

3

Der beste Nicht-sgi, nicht-Boost-STL-Lösung ist wie so map :: Iterator zu verlängern:

template<class map_type> 
class key_iterator : public map_type::iterator 
{ 
public: 
    typedef typename map_type::iterator map_iterator; 
    typedef typename map_iterator::value_type::first_type key_type; 

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ; 

    key_type& operator *() 
    { 
     return map_type::iterator::operator*().first; 
    } 
}; 

// helpers to create iterators easier: 
template<class map_type> 
key_iterator<map_type> key_begin(map_type& m) 
{ 
    return key_iterator<map_type>(m.begin()); 
} 
template<class map_type> 
key_iterator<map_type> key_end(map_type& m) 
{ 
    return key_iterator<map_type>(m.end()); 
} 

und dann nutzen sie etwa so:

 map<string,int> test; 
     test["one"] = 1; 
     test["two"] = 2; 

     vector<string> keys; 

//  // method one 
//  key_iterator<map<string,int> > kb(test.begin()); 
//  key_iterator<map<string,int> > ke(test.end()); 
//  keys.insert(keys.begin(), kb, ke); 

//  // method two 
//  keys.insert(keys.begin(), 
//   key_iterator<map<string,int> >(test.begin()), 
//   key_iterator<map<string,int> >(test.end())); 

     // method three (with helpers) 
     keys.insert(keys.begin(), key_begin(test), key_end(test)); 

     string one = keys[0]; 
+0

Ich überlasse es dem Leser, auch den const_iterator zu erstellen und Iteratoren umzukehren, falls/falls erforderlich. – Marius

35

C++ 0x hat uns eine weitere, sehr gute Lösung gegeben:

std::vector<int> keys; 

std::transform(
    m_Inputs.begin(), 
    m_Inputs.end(), 
    std::back_inserter(keys), 
    [](const std::map<int,int>::value_type &pair){return pair.first;}); 
+17

Aus meiner Sicht gibt es nichts ausgezeichnetes daran. Std :: Vektor Schlüssel; keys.reserve (m_Inputs.size()); für (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Ist viel besser als die kryptische Transformation. Auch in Bezug auf die Leistung. Dieses hier ist besser. – Jagannath

+2

Sie können hier auch die Größe von Schlüsseln reservieren, wenn Sie eine vergleichbare Leistung wünschen. Verwenden Sie die Transformation, wenn Sie eine for-Schleife vermeiden möchten. – DanDan

+3

will nur hinzufügen - kann [] (const auto & paar) –

49

Es gibt eine boost range adaptor für diesen Zweck:

Es gibt einen ähnlichen map_values ​​Bereich Adapter zum Extrahieren der Werte.

+0

Sehr saubere, kompakte und elegante Lösung. Liebe es ! –

+1

Leider scheint Boost :: Adaptors erst ab Boost 1.43 verfügbar zu sein. Die aktuelle stabile Version von Debian (Squeeze) bietet nur Boost 1.42 –

+2

Das ist schade. Boost 1.42 wurde im Februar 2010 veröffentlicht, über 2,5 Jahre vor Squeeze. – Alastair

110
//c++0x too 
std::map<int,int> mapints; 
std::vector<int> vints; 
vints.reserve(mapints.size()); 
for(auto const& imap: mapints) 
    vints.push_back(imap.first); 
+0

Es gibt keine C++ 0x-Tag in der Frage. – Jagannath

+32

C++ 11 ist der aktuelle C++ - Standard. C++ == C++ 11 –

+9

Es war nicht zum Zeitpunkt, als die Frage gestellt wurde, noch zu dem Zeitpunkt, als diese Antwort gegeben wurde. –

2

Bit eines C++ 11 nehmen:

std::map<uint32_t, uint32_t> items; 
std::vector<uint32_t> itemKeys; 
for (auto & kvp : items) 
{ 
    itemKeys.emplace_back(kvp.first); 
    std::cout << kvp.first << std::endl; 
} 
1

Hier ist eine nette Funktion Vorlage mit C++ 11 Magie, die sich für beide std :: map, std :: unordered_map:

template<template <typename...> class MAP, class KEY, class VALUE> 
std::vector<KEY> 
keys(const MAP<KEY, VALUE>& map) 
{ 
    std::vector<KEY> result; 
    result.reserve(map.size()); 
    for(const auto& it : map){ 
     result.emplace_back(it.first); 
    } 
    return result; 
} 

bestellen hier aus: http://ideone.com/lYBzpL

3

@ DanDan Antwort, C++ 11:

using namespace std; 
vector<int> keys; 

transform(begin(map_in), end(map_in), back_inserter(keys), 
      [](decltype(map_in)::value_type const& pair) { 
    return pair.first; 
}); 

und C++ 14 unter Verwendung von (wie von @ ivan.ukr angegeben) wir decltype(map_in)::value_type mit auto ersetzen können.

Verwandte Themen