web-dev-qa-db-de.com

Schlüssel in einer C++ - Map wiederholen

Gibt es eine Möglichkeit, über die Tasten zu iterieren, nicht die Paare einer C++ - Karte?

98
Bogdan Balan

Wenn Sie wirklich den Wert ausblenden müssen, den der "echte" Iterator zurückgibt (zum Beispiel, weil Sie Ihren Key-Iterator mit Standardalgorithmen verwenden möchten, damit er die Tasten anstelle der Paare verwendet), dann schauen Sie sich Boost's an transform_iterator .

[Tipp: Wenn Sie in der Boost-Dokumentation nach einer neuen Klasse suchen, lesen Sie zuerst die "Beispiele". Sie haben dann eine sportliche Chance, um herauszufinden, worüber in aller Welt der Rest spricht :-)]

68
Steve Jessop

map ist assoziativer Container. Daher ist der Iterator ein Schlüsselpaar val. WENN Sie nur Schlüssel benötigen, können Sie den Wertteil aus dem Paar ignorieren.

for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k =  iter->first;
//ignore value
//Value v = iter->second;
}

EDIT: : Wenn Sie nur die Schlüssel nach außen zeigen möchten, können Sie die Karte in Vektor oder Schlüssel konvertieren und freigeben.

97
aJ.

Mit C++ 11 ist die Iterationssyntax einfach. Sie durchlaufen immer noch Paare, aber der Zugriff auf den Schlüssel ist einfach.

#include <iostream>
#include <map>

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";
    }
}
74
John H.

Ohne Boost

Sie können dies tun, indem Sie einfach den STL-Iterator für diese Karte erweitern. Zum Beispiel eine Zuordnung von Strings 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 auch Diese Erweiterung in einer Vorlage ausführen verwenden, um eine allgemeinere Lösung zu erhalten. 

Sie verwenden Ihren Iterator genau so wie Sie einen Listen-Iterator verwenden würden, es sei denn, Sie durchlaufen die begin() und end() der Map.

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());
30
Ian

Mit C++ 17 können Sie eine strukturierte Bindung verwenden innerhalb einer bereichsbasierten for-Schleife (Anpassung John Hs Antwort entsprechend):

#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 Variable value deklarieren, obwohl Sie sie nicht verwenden ( std::ignore , wie man es für = verwenden würde std::tie(..) funktioniert nicht, siehe diese Diskussion ).

Einige Compiler warnen Sie daher möglicherweise vor der nicht verwendeten Variablen value! Warnungen zur Kompilierungszeit in Bezug auf nicht verwendete Variablen sind für jeden Produktionscode in meinem Kopf ein absolutes Muss. Dies gilt möglicherweise nicht für bestimmte Compilerversionen.

20
Elmar

Du suchst nach map_keys , damit kannst du Dinge schreiben wie

BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
{
  // do something with key
}
10
rodrigob

Unter der allgemeineren Lösung, auf die Ian verwiesen hat ...

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

9
degski

Wenn keine explizite begin und end benötigt wird, dh für Range-Looping, können die Loopover-Keys (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;
4
Darko Veberic

Hier ist ein Beispiel, wie dies mit Boost's transform_iterator geschieht.

#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;
  }
}
4
algal

Wenn Sie einen Iterator benötigen, der nur die Schlüssel zurückgibt, müssen Sie den Iterator von map in Ihre eigene Klasse einbetten, die die gewünschte Schnittstelle bietet. Sie können eine neue Iterator-Klasse von Grund auf neu deklarieren, indem Sie hier verwenden, um vorhandene Helfer-Konstrukte zu verwenden. Diese Antwort zeigt, wie Sie mit Boosts transform_iterator den Iterator in einen solchen einschließen, der nur die Werte/Schlüssel zurückgibt.

3
sth

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;  
   .....
}
3
Naveen

Diese Antwort ist wie die von rodrigob, außer ohne den BOOST_FOREACH. Sie können stattdessen den Bereich von c ++ verwenden.

#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;
     }
}
2
user4608041

Du könntest

  • erstellen Sie eine benutzerdefinierte Iterator-Klasse, indem Sie den std::map<K,V>::iterator aggregieren.
  • verwenden Sie std::transform Ihrer map.begin() zu map.end() mit einem boost::bind( &pair::second, _1 )-Funktor
  • ignorieren Sie einfach das ->second-Member, während Sie mit einer for-Schleife durchlaufen.
2
xtofl

Ich weiß, dass dies Ihre Frage nicht beantwortet, aber eine Option, die Sie sich ansehen möchten, besteht einfach darin, dass zwei Vektoren mit demselben Index "verknüpfte" Informationen sind. 

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 for-for-Schleife über vName.size () durch, und wenn Sie sie finden, ist dies der Index für vNameCount, nach dem Sie suchen. 

Sicher, dies gibt Ihnen möglicherweise nicht die gesamte Funktionalität der Karte, und je nachdem kann es besser oder besser sein, aber es ist möglicherweise einfacher, wenn Sie die Schlüssel nicht kennen und nicht zu viel Verarbeitung hinzufügen.

Denken Sie daran, wenn Sie aus einem hinzufügen oder löschen, müssen Sie es aus dem anderen tun oder die Dinge werden verrückt, heh: P

1
John Doe

Ohne Boost könnten Sie es so machen. Es wäre schön, wenn Sie anstelle von getKeyIterator () einen Besetzungsoperator schreiben könnten, aber ich kann ihn nicht kompilieren.

#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());
    }
}
0
Jack Haughton

Für die Nachwelt, und da ich versucht habe, einen Bereich zu erstellen, können Sie boost :: adapters :: transform

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 t.second im Lambda.

0
ipapadop

Ich habe Ians Antwort angenommen, um mit allen Kartentypen zu arbeiten, und eine Referenz für operator* zurückgegeben.

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;
    }
};
0
Gabriel Huber

Hier gibt es viele gute Antworten. Im Folgenden wird ein Ansatz mit ein paar davon beschrieben, mit dem 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, 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);
}
0
Superfly Jon