Wie kann man alle Schlüssel (oder Werte) von einer std :: map abrufen und in einen Vektor einfügen?

246

Dies ist einer der möglichen Wege, wie ich herauskomme:

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 von der Karte abrufen, indem wir einen anderen Funktor RetrieveValues ​​definieren .

Gibt es eine andere Möglichkeit, dies einfach zu erreichen? (Ich frage mich immer, warum std :: map keine Mitgliedsfunktion enthält, damit wir dies tun können.)

Owen
quelle
10
Ihre Lösung ist die beste ...
Linello
4
Der einzige Gedanke, den ich hinzufügen würde, ist keys.reserve(m.size());.
Galik

Antworten:

176

Während Ihre Lösung funktionieren sollte, kann es je nach Kenntnisstand Ihrer Programmierkollegen schwierig sein, sie zu lesen. Darüber hinaus werden Funktionen von der Anrufstelle entfernt. Das kann die Wartung etwas erschweren.

Ich bin mir nicht sicher, ob Ihr Ziel darin besteht, die Schlüssel in einen Vektor zu bringen oder sie zu drucken, damit ich beides mache. Sie können so etwas 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 verwenden:

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 weniger getippt wird und sehr explizit ist, was sie tut.

Jere.Jones
quelle
1
Ich würde nach meiner Google-Suche wieder hier landen. Ihre ist die Antwort, die ich bevorzuge :)
mpen
4
@Jere - Hast du tatsächlich mit gearbeitet BOOST_FOREACH? Der Code, den Sie hier vorschlagen, ist völlig falsch
Manuel
2
@Jamie - das ist ein anderer Weg, aber die Boost-Dokumente zeigen die Angabe der Variablen und ihres Typs vor BOOST_FOREACH, wenn der Typ ein Komma enthält. Sie zeigen auch die Eingabe. Also, ich bin verwirrt, was ist mit meinem Code falsch?
Jere.Jones
17
Seltsamerweise wäre es nicht sinnvoll, die Größe des Vektors zu ändern, um eine Größenänderung zu verhindern?
Alan
2
Vergessen Sie nicht, dies zu tun v.reserve(m.size()), um zu vermeiden, dass die Größe des Vektors während der Übertragung geändert wird.
Brian White
157
//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);
Juan
quelle
4
Nett. Vergiss es it = ...begin(); it != ...end. Am schönsten wäre natürlich std :: map mit einem Methodenschlüssel (), der diesen Vektor
zurückgibt
2
@ BenHymers: Es scheint mir, dass diese Antwort gegeben wurde answered Mar 13 '12 at 22:33, einige Monate nachdem C ++ 11 zu C ++ wurde.
Sebastian Mach
37
@BenHymers, aber es ist für jeden von Nutzen, der die Frage jetzt liest, worum es bei SO geht - nicht nur um dem Fragesteller, sondern allen anderen zu helfen.
Luchian Grigore
9
for (auto & imap) ist genauer, da kein Kopiervorgang erfolgt.
HelloWorld
2
@StudentT, noch besser , for(auto const & imap : mapints).
cp.engr
61

Zu diesem Zweck gibt es einen Boost-Range-Adapter :

vector<int> keys;
// Retrieve all keys
boost::copy(m | boost::adaptors::map_keys, std::back_inserter(keys));

Es gibt einen ähnlichen Bereichsadapter für map_values ​​zum Extrahieren der Werte.

Alastair
quelle
1
Leider scheint boost::adaptorses nicht verfügbar zu sein, bis Boost 1.43. Die aktuelle stabile Version von Debian (Squeeze) bietet nur Boost 1.42
Mickaël Le Baillif
2
Das ist schade. Boost 1.42 wurde im Februar 2010 veröffentlicht, mehr als 2,5 Jahre vor Squeeze.
Alastair
Sollten Squeeze Updates und / oder das Backports-Repo zu diesem Zeitpunkt nicht Boost 1.44 anbieten?
Luis Machuca
In welchem ​​Boost-Header ist der definiert?
James Wierzba
1
Siehe das verknüpfte Dokument, es ist definiert inboost/range/adaptor/map.hpp
Alastair
46

C ++ 0x hat uns eine weitere, hervorragende 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;});
DanDan
quelle
22
Meiner Meinung nach ist daran nichts Besonderes. std :: vector <int> keys; keys.reserve (m_Inputs.size ()); for (auto keyValue: m_Inputs) {keys.push_back (keyValue.first); } Ist weitaus besser als die kryptische Transformation. Auch in Bezug auf die Leistung. Dieses hier ist besser.
Jagannath
5
Sie können auch hier die Größe der Schlüssel reservieren, wenn Sie eine vergleichbare Leistung wünschen. Verwenden Sie die Transformation, wenn Sie eine for-Schleife vermeiden möchten.
DanDan
4
Ich
@ ivan.ukr Welchen Compiler verwendest du? Diese Syntax ist hier nicht erlaubt: 'const auto &': Ein Parameter kann keinen Typ haben, der 'auto' enthält
Gobe
4
@ ivan.ukr Auto-Parameter in Lambda ist c ++ 14
Roalz
16

Die Antwort von @ DanDan unter Verwendung von C ++ 11 lautet:

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 unter Verwendung von C ++ 14 (wie bereits von @ ivan.ukr) können wir ersetzen decltype(map_in)::value_typemit auto.

James Hirschorn
quelle
5
Sie könnten keys.reserve(map_in.size());für die Effizienz hinzufügen .
Galik
Ich finde, dass die Transformationsmethode tatsächlich mehr Code benötigt als die for-Schleife.
user1633272
const kann hinter den Typ gesetzt werden! Das vergesse ich fast.
Zhang
12

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

Chris Jester-Young
quelle
10

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

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
}
Brian R. Bondy
quelle
10

Basierend auf der @ Rusty-Parks-Lösung, jedoch in c ++ 17:

std :: map <int, int> items;
std :: vector <int> itemKeys;

für (const auto & [Taste, ignoriert]: Elemente)
{
    itemKeys.push_back (Schlüssel);
}}
Madiyar
quelle
Ich denke nicht, dass std::ignoreman auf diese Weise in strukturierten Bindungen verwendet werden kann. Ich erhalte einen Kompilierungsfehler. Es sollte ausreichen, nur eine reguläre Variable zu verwenden, z. B. ignoreddie einfach nicht verwendet wird.
jb
1
@jb Danke. In der Tat std::ignoreist zur Verwendung mit, std::tieaber nicht mit strukturellen Bindungen vorgesehen. Ich habe meinen Code aktualisiert.
Madiyar
9

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

#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") );

Persönlich denke ich nicht, dass dieser Ansatz in diesem Fall so sauber ist wie der BOOST_FOREACH-Ansatz, aber boost :: lambda kann in anderen Fällen wirklich sauber sein.

paxos1977
quelle
7

Wenn Sie über Boost verfügen, verwenden Sie transform_iterator, um zu vermeiden, dass eine temporäre Kopie der Schlüssel erstellt wird.

Marcelo Cantos
quelle
7

Ein bisschen wie ein C ++ 11-Take:

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;
}
Rostige Parks
quelle
5

Hier ist eine nette Funktionsvorlage mit C ++ 11-Magie, die sowohl für std :: map als auch für std :: unordered_map funktioniert:

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

Überprüfen Sie es hier: http://ideone.com/lYBzpL

Clemens Sielaff
quelle
4

Die beste STL-Lösung ohne SGI und ohne Boost besteht darin, map :: iterator wie folgt zu erweitern:

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 benutze sie 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];
Marius
quelle
1
Ich überlasse es dem Leser, auch den const_iterator zu erstellen und bei Bedarf Iteratoren umzukehren.
Marius
-1

Mit Atomkartenbeispiel

#include <iostream>
#include <map>
#include <vector> 
#include <atomic>

using namespace std;

typedef std::atomic<std::uint32_t> atomic_uint32_t;
typedef std::map<int, atomic_uint32_t> atomic_map_t;

int main()
{
    atomic_map_t m;

    m[4] = 456;
    m[2] = 45678;

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

    return 0;
}
Deniz Babat
quelle
-2

Etwas ähnlich wie eines der hier gezeigten Beispiele, vereinfacht aus std::mapSicht der Nutzung.

template<class KEY, class VALUE>
std::vector<KEY> getKeys(const std::map<KEY, VALUE>& map)
{
    std::vector<KEY> keys(map.size());
    for (const auto& it : map)
        keys.push_back(it.first);
    return keys;
}

Verwenden Sie wie folgt:

auto keys = getKeys(yourMap);
TarmoPikaro
quelle
2
Hey, ich weiß, dass diese Antwort alt ist, aber sie ist auch falsch. Das Initialisieren mit size map.size()bedeutet die doppelte Rückgabe der Vektorgröße. Bitte
korrigieren
-3

(Ich frage mich immer, warum std :: map keine Mitgliedsfunktion enthält, damit wir dies tun können.)

Weil es nicht besser geht als Sie. 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.

DrPizza
quelle
8
Es gibt andere Gründe als die Effizienz einer Bibliothek, eine Methode bereitzustellen, z. B. die Funktionalität "Batterien enthalten" und eine kohärente, gekapselte API. Obwohl zugegebenermaßen keiner dieser 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 arbeiten zu können: Es hängt davon ab, wofür Sie es verwenden.
Andybuckley
4
Nach dieser Überlegung sollten wir nicht haben, empty()weil es als implementiert werden kann size() == 0.
GD1
1
Was @ gd1 gesagt hat. Während es in einer Klasse nicht viel funktionale Redundanz geben sollte, ist es keine gute Idee, auf absolut Null zu bestehen, IMO - zumindest bis C ++ es uns ermöglicht, freie Funktionen in Methoden zu "segnen".
Einpoklum
1
In älteren Versionen von C ++ gab es Container, für die empty () und size () vernünftigerweise unterschiedliche Leistungsgarantien haben konnten, und ich denke, die Spezifikation war ausreichend locker, um dies zu ermöglichen (insbesondere verknüpfte Listen, die zeitlich konstanten Spleiß () boten). . Daher war es sinnvoll, sie zu entkoppeln. Ich denke jedoch nicht, dass diese Diskrepanz mehr zulässig ist.
DrPizza
Genau. C ++ wird std::map<T,U>als Container von Paaren behandelt. In Python dictverhält sich a wie seine Schlüssel, wenn es wiederholt wird, aber Sie können sagen d.items(), dass Sie das C ++ - Verhalten erhalten möchten. Python bietet auch d.values(). std::map<T,U>sicherlich könnte liefern keys()und values()Methode , die ein Objekt zurück, das hat begin()und end()die Iteratoren über die Schlüssel und Werte liefern.
Ben