Czy istnieje sposób na iterację po kluczach, a nie na parach map C ++?
Czy istnieje sposób na iterację po kluczach, a nie na parach map C ++?
Odpowiedzi:
Jeśli naprawdę potrzebujesz ukryć wartość zwracaną przez „prawdziwy” iterator (na przykład dlatego, że chcesz używać swojego iteratora klucza ze standardowymi algorytmami, aby działały na klawiszach zamiast na parach), spójrz na funkcję Boost transform_iterator .
[Wskazówka: patrząc na dokumentację Boost dla nowej klasy, najpierw przeczytaj "przykłady" na końcu. Masz wtedy sportową szansę dowiedzieć się, o czym u licha mówi reszta :-)]
mapa to kontener asocjacyjny. Stąd iterator jest parą kluczy, val. JEŚLI potrzebujesz tylko kluczy, możesz zignorować część wartości z pary.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
EDYCJA:: W przypadku, gdy chcesz wystawić tylko klucze na zewnątrz, możesz przekonwertować mapę na wektor lub klucze i wystawić.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
W C ++ 11 składnia iteracji jest prosta. Nadal iterujesz po parach, ale dostęp tylko do klucza jest łatwy.
#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";
}
}
Możesz to zrobić, po prostu rozszerzając iterator STL dla tej mapy. Na przykład mapowanie ciągów znaków na wartości int:
#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; }
};
Możesz również wykonać to rozszerzenie w szablonie , aby uzyskać bardziej ogólne rozwiązanie.
Używasz iteratora dokładnie tak, jak używasz iteratora listy, z wyjątkiem tego, że iterujesz po mapach begin()
i 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
itd.
W C ++ 17 możesz użyć strukturalnego powiązania wewnątrz pętli for opartej na zakresie (dostosowując odpowiednio odpowiedź Johna H. ):
#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';
}
}
Niestety standard C ++ 17 wymaga zadeklarowania value
zmiennej, nawet jeśli jej nie używasz ( std::ignore
ponieważ std::tie(..)
nie działa, zobacz tę dyskusję ).
Dlatego niektóre kompilatory mogą ostrzec Cię o nieużywanej value
zmiennej! Ostrzeżenia w czasie kompilacji dotyczące nieużywanych zmiennych są nie do pomyślenia w przypadku żadnego kodu produkcyjnego. Dlatego może to nie mieć zastosowania w przypadku niektórych wersji kompilatora.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Poniżej bardziej ogólne rozwiązanie oparte na szablonach, do którego odniósł się Ian ...
#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; }
};
Wszystkie kredyty należą się Ianowi ... Dzięki, Ian.
Oto przykład, jak to zrobić za pomocą transform_iterator Boost
#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;
}
}
Gdy nie jest to jawne begin
i nie end
jest potrzebne, np. Do zapętlania zakresu, pętlę po kluczach (pierwszy przykład) lub wartości (drugi przykład) można uzyskać za pomocą
#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;
Chcesz to zrobić?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Jeśli potrzebujesz iteratora, który po prostu zwraca klucze, musisz zawinąć iterator mapy we własnej klasie, która zapewnia żądany interfejs. Możesz zadeklarować nową klasę iteratora od podstaw, tak jak tutaj , używając istniejących konstrukcji pomocniczych. Ta odpowiedź pokazuje, jak używać funkcji Boost, transform_iterator
aby zawinąć iterator w taki, który zwraca tylko wartości / klucze.
Ta odpowiedź jest jak rodrigob, z wyjątkiem braku BOOST_FOREACH
. Zamiast tego możesz użyć zakresu c ++ opartego na.
#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;
}
}
Bez Boost można to zrobić w ten sposób. Byłoby miło, gdybyś mógł napisać operator rzutowania zamiast metody getKeyIterator (), ale nie mogę go skompilować.
#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());
}
}
Dla potomnych, a ponieważ próbowałem znaleźć sposób na stworzenie zakresu, alternatywą jest użycie boost :: adapters :: transform
Oto mały przykład:
#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;
}
Jeśli chcesz iterować po wartościach, użyj t.second
w lambdzie.
Wiele dobrych odpowiedzi tutaj, poniżej jest podejście wykorzystujące kilka z nich, które pozwalają napisać to:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Jeśli zawsze tego chciałeś, oto kod dla 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);
}
Zaadoptowałem odpowiedź Iana do pracy ze wszystkimi typami map i naprawiłem zwracanie odniesienia dla 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;
}
};
Wiem, że to nie odpowiada na twoje pytanie, ale jedną z opcji, na którą możesz chcieć spojrzeć, jest po prostu posiadanie dwóch wektorów z tym samym indeksem będącym informacją „połączoną”.
Więc w ...
std::vector<std::string> vName;
std::vector<int> vNameCount;
jeśli chcesz liczyć nazwy według nazwy, po prostu wykonaj szybką pętlę for przez vName.size (), a kiedy ją znajdziesz, jest to indeks dla vNameCount, którego szukasz.
Jasne, może to nie zapewniać całej funkcjonalności mapy, a zależność może być lepsza lub nie, ale może być łatwiej, jeśli nie znasz kluczy i nie powinieneś dodawać zbyt dużego przetwarzania.
Pamiętaj tylko, że kiedy dodajesz / usuwasz z jednego, musisz to zrobić z drugiego, inaczej sprawy zwariują heh: P