it-swarm.com.de

Wie rufe ich alle Schlüssel (oder Werte) von einer std :: map ab und füge sie in einen Vektor ein?

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

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.)

213
Owen

Während Ihre Lösung funktionieren sollte, kann es abhängig von den Fähigkeiten Ihrer Programmierkollegen schwierig sein, sie zu lesen. Darüber hinaus wird die Funktionalität von der Anrufsite 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 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 es sehr explizit ist, was sie tut.

158
Jere.Jones
//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);
135
Juan

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.

58
Alastair

C++ 0x hat uns eine weitere, exzellente 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;});
43
DanDan

@ DanDans Antwort 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 mit C++ 14 (wie von @ ivan.ukr angegeben) können wir decltype(map_in)::value_type durch auto ersetzen.

12
James Hirschorn

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

10

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

#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, dass dieser Ansatz in diesem Fall nicht so sauber ist wie der BOOST_FOREACH-Ansatz, aber boost :: lambda kann in anderen Fällen wirklich sauber sein.

9
paxos1977

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

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

7
Marcelo Cantos

Ein bisschen wie 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;
}
6
Rusty Parks

Sie können den vielseitigen boost :: transform_iterator verwenden. Mit dem transform_iterator können Sie die iterierten Werte transformieren, zum Beispiel in unserem Fall, wenn Sie nur mit den Schlüsseln und nicht mit den Werten arbeiten möchten. Siehe http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/transform_iterator.html#example

5
amit

Hier ist eine Nice-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

4
Clemens Sielaff

Die beste nicht-sgi, nicht-boost STL-Lösung besteht darin, map :: iterator folgendermaßen 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];
4
Marius

Etwas ähnlich zu einem der Beispiele hier, vereinfacht aus std::map Nutzungsperspektive.

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);
0
TarmoPikaro

Basierend auf @ rusty-parks-Lösung, aber in c ++ 17:

std::map<uint32_t, uint32_t> items;
std::vector<uint32_t> itemKeys;
for (auto const& [key, std:ignore] : items) {
  itemKeys.emplace_back(key);
}
0
Madiyar