Gibt es eine Möglichkeit, über die Schlüssel zu iterieren, nicht über die Paare einer C ++ - Zuordnung?
Gibt es eine Möglichkeit, über die Schlüssel zu iterieren, nicht über die Paare einer C ++ - Zuordnung?
Antworten:
Wenn Sie den Wert, den der "echte" Iterator zurückgibt, wirklich ausblenden müssen (z. B. weil Sie Ihren Schlüsseliterator mit Standardalgorithmen verwenden möchten, damit diese auf den Schlüsseln anstelle der Paare arbeiten), schauen Sie sich Boosts an transform_iterator .
[Tipp: Wenn Sie sich die Boost-Dokumentation für eine neue Klasse ansehen, lesen Sie zuerst die "Beispiele" am Ende. Sie haben dann eine sportliche Chance herauszufinden, wovon um alles in der Welt der Rest spricht :-)]
Karte ist assoziativer Container. Daher ist der Iterator ein Schlüsselpaar, val. Wenn Sie nur Schlüssel benötigen, können Sie den Werteteil des Paares ignorieren.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
BEARBEITEN :: Wenn Sie nur die Schlüssel nach außen verfügbar machen möchten, können Sie die Karte in einen Vektor oder Schlüssel konvertieren und verfügbar machen.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
Mit C ++ 11 ist die Iterationssyntax einfach. Sie iterieren immer noch über Paare, aber der Zugriff auf nur den Schlüssel ist einfach.
#include <iostream>
#include <map>
int main()
{
std::map<std::string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;
for ( const auto &myPair : myMap ) {
std::cout << myPair.first << "\n";
}
}
Sie können dies tun, indem Sie einfach den STL-Iterator für diese Karte erweitern. Zum Beispiel eine Zuordnung von Zeichenfolgen zu Ints:
#include <map>
typedef map<string, int> ScoreMap;
typedef ScoreMap::iterator ScoreMapIterator;
class key_iterator : public ScoreMapIterator
{
public:
key_iterator() : ScoreMapIterator() {};
key_iterator(ScoreMapIterator s) : ScoreMapIterator(s) {};
string* operator->() { return (string* const)&(ScoreMapIterator::operator->()->first); }
string operator*() { return ScoreMapIterator::operator*().first; }
};
Sie können diese Erweiterung auch in einer Vorlage ausführen , um eine allgemeinere Lösung zu erhalten.
Sie verwenden Ihren Iterator genau so, wie Sie einen Listeniterator verwenden würden, außer dass Sie über die Karten begin()
und iterieren end()
.
ScoreMap m;
m["jim"] = 1000;
m["sally"] = 2000;
for (key_iterator s = m.begin(); s != m.end(); ++s)
printf("\n key %s", s->c_str());
template<typename C> class key_iterator : public C::iterator
, etc
Mit C ++ 17 können Sie eine strukturierte Bindung innerhalb einer bereichsbasierten for-Schleife verwenden (indem Sie die Antwort von John H. entsprechend anpassen ):
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;
for ( const auto &[key, value]: myMap ) {
std::cout << key << '\n';
}
}
Leider verlangt der C ++ 17-Standard, dass Sie die value
Variable deklarieren , obwohl Sie sie nicht verwenden ( std::ignore
wie man es verwenden würde, std::tie(..)
funktioniert nicht, siehe diese Diskussion ).
Einige Compiler warnen Sie daher möglicherweise vor der nicht verwendeten value
Variablen! Warnungen zur Kompilierungszeit in Bezug auf nicht verwendete Variablen sind für mich kein Problem für Produktionscode. Dies gilt möglicherweise nicht für bestimmte Compilerversionen.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Unten die allgemeinere Vorlagenlösung, auf die sich Ian bezog ...
#include <map>
template<typename Key, typename Value>
using Map = std::map<Key, Value>;
template<typename Key, typename Value>
using MapIterator = typename Map<Key, Value>::iterator;
template<typename Key, typename Value>
class MapKeyIterator : public MapIterator<Key, Value> {
public:
MapKeyIterator ( ) : MapIterator<Key, Value> ( ) { };
MapKeyIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };
Key *operator -> ( ) { return ( Key * const ) &( MapIterator<Key, Value>::operator -> ( )->first ); }
Key operator * ( ) { return MapIterator<Key, Value>::operator * ( ).first; }
};
template<typename Key, typename Value>
class MapValueIterator : public MapIterator<Key, Value> {
public:
MapValueIterator ( ) : MapIterator<Key, Value> ( ) { };
MapValueIterator ( MapIterator<Key, Value> it_ ) : MapIterator<Key, Value> ( it_ ) { };
Value *operator -> ( ) { return ( Value * const ) &( MapIterator<Key, Value>::operator -> ( )->second ); }
Value operator * ( ) { return MapIterator<Key, Value>::operator * ( ).second; }
};
Alle Credits gehen an Ian ... Danke Ian.
Hier ist ein Beispiel, wie dies mit dem transform_iterator von Boost gemacht wird
#include <iostream>
#include <map>
#include <iterator>
#include "boost/iterator/transform_iterator.hpp"
using std::map;
typedef std::string Key;
typedef std::string Val;
map<Key,Val>::key_type get_key(map<Key,Val>::value_type aPair) {
return aPair.first;
}
typedef map<Key,Val>::key_type (*get_key_t)(map<Key,Val>::value_type);
typedef map<Key,Val>::iterator map_iterator;
typedef boost::transform_iterator<get_key_t, map_iterator> mapkey_iterator;
int main() {
map<Key,Val> m;
m["a"]="A";
m["b"]="B";
m["c"]="C";
// iterate over the map's (key,val) pairs as usual
for(map_iterator i = m.begin(); i != m.end(); i++) {
std::cout << i->first << " " << i->second << std::endl;
}
// iterate over the keys using the transformed iterators
mapkey_iterator keybegin(m.begin(), get_key);
mapkey_iterator keyend(m.end(), get_key);
for(mapkey_iterator i = keybegin; i != keyend; i++) {
std::cout << *i << std::endl;
}
}
Wenn dies nicht explizit begin
und end
erforderlich ist, dh für eine Bereichsschleife, kann die Schleife über Schlüssel (erstes Beispiel) oder Werte (zweites Beispiel) mit erhalten werden
#include <boost/range/adaptors.hpp>
map<Key, Value> m;
for (auto k : boost::adaptors::keys(m))
cout << k << endl;
for (auto v : boost::adaptors::values(m))
cout << v << endl;
Du willst das machen?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Wenn Sie einen Iterator benötigen, der nur die Schlüssel zurückgibt, müssen Sie den Iterator der Karte in Ihre eigene Klasse einbinden, die die gewünschte Schnittstelle bereitstellt. Sie können wie hier eine neue Iteratorklasse von Grund auf deklarieren , um vorhandene Hilfskonstrukte zu verwenden. Diese Antwort zeigt, wie Boosts verwendet werden transform_iterator
, um den Iterator in einen Iterator zu verpacken, der nur die Werte / Schlüssel zurückgibt.
Du könntest
std::map<K,V>::iterator
std::transform
Ihres map.begin()
zu map.end()
mit einem boost::bind( &pair::second, _1 )
Funktor->second
Mitglied, während Sie mit einer for
Schleife iterieren .Diese Antwort ist wie die von Rodrigob, außer ohne die BOOST_FOREACH
. Sie können stattdessen den Bereich von c ++ verwenden, der auf basiert.
#include <map>
#include <boost/range/adaptor/map.hpp>
#include <iostream>
template <typename K, typename V>
void printKeys(std::map<K,V> map){
for(auto key : map | boost::adaptors::map_keys){
std::cout << key << std::endl;
}
}
Ohne Boost könnten Sie es so machen. Es wäre schön, wenn Sie anstelle von getKeyIterator () einen Cast-Operator schreiben könnten, aber ich kann ihn nicht zum Kompilieren bringen.
#include <map>
#include <unordered_map>
template<typename K, typename V>
class key_iterator: public std::unordered_map<K,V>::iterator {
public:
const K &operator*() const {
return std::unordered_map<K,V>::iterator::operator*().first;
}
const K *operator->() const {
return &(**this);
}
};
template<typename K,typename V>
key_iterator<K,V> getKeyIterator(typename std::unordered_map<K,V>::iterator &it) {
return *static_cast<key_iterator<K,V> *>(&it);
}
int _tmain(int argc, _TCHAR* argv[])
{
std::unordered_map<std::string, std::string> myMap;
myMap["one"]="A";
myMap["two"]="B";
myMap["three"]="C";
key_iterator<std::string, std::string> &it=getKeyIterator<std::string,std::string>(myMap.begin());
for (; it!=myMap.end(); ++it) {
printf("%s\n",it->c_str());
}
}
Für die Nachwelt und da ich versucht habe, einen Weg zum Erstellen eines Bereichs zu finden, besteht eine Alternative darin, boost :: adapters :: transform zu verwenden
Hier ist ein kleines Beispiel:
#include <boost/range/adaptor/transformed.hpp>
#include <iostream>
#include <map>
int main(int argc, const char* argv[])
{
std::map<int, int> m;
m[0] = 1;
m[2] = 3;
m[42] = 0;
auto key_range =
boost::adaptors::transform(
m,
[](std::map<int, int>::value_type const& t)
{ return t.first; }
);
for (auto&& key : key_range)
std::cout << key << ' ';
std::cout << '\n';
return 0;
}
Wenn Sie die Werte durchlaufen möchten, verwenden Sie sie t.second
im Lambda.
Hier finden Sie viele gute Antworten. Im Folgenden finden Sie einige Ansätze, mit denen Sie Folgendes schreiben können:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Wenn Sie das schon immer wollten, dann ist hier der Code für MapKeys ():
template <class MapType>
class MapKeyIterator {
public:
class iterator {
public:
iterator(typename MapType::iterator it) : it(it) {}
iterator operator++() { return ++it; }
bool operator!=(const iterator & other) { return it != other.it; }
typename MapType::key_type operator*() const { return it->first; } // Return key part of map
private:
typename MapType::iterator it;
};
private:
MapType& map;
public:
MapKeyIterator(MapType& m) : map(m) {}
iterator begin() { return iterator(map.begin()); }
iterator end() { return iterator(map.end()); }
};
template <class MapType>
MapKeyIterator<MapType> MapKeys(MapType& m)
{
return MapKeyIterator<MapType>(m);
}
Ich habe Ians Antwort übernommen, um mit allen Kartentypen zu arbeiten, und die Rückgabe einer Referenz für behoben operator*
template<typename T>
class MapKeyIterator : public T
{
public:
MapKeyIterator() : T() {}
MapKeyIterator(T iter) : T(iter) {}
auto* operator->()
{
return &(T::operator->()->first);
}
auto& operator*()
{
return T::operator*().first;
}
};
Ich weiß, dass dies Ihre Frage nicht beantwortet, aber eine Option, die Sie möglicherweise prüfen möchten, besteht darin, nur zwei Vektoren mit demselben Index als "verknüpfte" Informationen zu verwenden.
Also in ..
std::vector<std::string> vName;
std::vector<int> vNameCount;
Wenn Sie die Anzahl der Namen nach Namen festlegen möchten, führen Sie einfach eine schnelle for-Schleife über vName.size () durch. Wenn Sie diese finden, ist dies der Index für vNameCount, den Sie suchen.
Sicher, dies gibt Ihnen möglicherweise nicht die gesamte Funktionalität der Karte, und je nachdem kann es besser sein oder auch nicht, aber es ist möglicherweise einfacher, wenn Sie die Schlüssel nicht kennen und nicht zu viel Verarbeitung hinzufügen sollten.
Denken Sie daran, wenn Sie von einem hinzufügen / löschen, müssen Sie es von dem anderen tun, sonst werden die Dinge verrückt heh: P.