Anahtarlar üzerinde yineleme yapmanın bir yolu var mı, C ++ haritasının çiftleri değil mi?
Anahtarlar üzerinde yineleme yapmanın bir yolu var mı, C ++ haritasının çiftleri değil mi?
Yanıtlar:
"Gerçek" yineleyicinin döndürdüğü değeri gerçekten gizlemeniz gerekiyorsa (örneğin, anahtar yineleyicinizi standart algoritmalarla kullanmak istediğiniz için, böylece çiftler yerine anahtarlar üzerinde çalışırlar), o zaman Boost'un transform_iterator .
[İpucu: Yeni bir sınıf için Boost belgelerine bakarken, önce en sondaki "örnekleri" okuyun. O halde, geri kalanının ne hakkında konuştuğunu anlamak için spor bir şansınız olur :-)]
harita ilişkisel kapsayıcıdır. Dolayısıyla, yineleyici bir çift anahtardır, val. Yalnızca anahtarlara ihtiyacınız varsa, çiftin değer kısmını göz ardı edebilirsiniz.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
DÜZENLEME:: Yalnızca anahtarları dışarıda göstermek istiyorsanız, haritayı vektöre veya anahtarlara dönüştürebilir ve açığa çıkarabilirsiniz.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
C ++ 11 ile yineleme sözdizimi basittir. Hala çiftler üzerinde yinelersiniz, ancak yalnızca anahtara erişmek kolaydır.
#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";
}
}
Bunu, o harita için STL yineleyicisini genişleterek yapabilirsiniz. Örneğin, dizelerin tamsalara eşlenmesi:
#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; }
};
Daha genel bir çözüm için bu uzantıyı bir şablonda da gerçekleştirebilirsiniz .
Yineleyicinizi tam olarak bir liste yineleyici gibi kullanırsınız, tek fark haritanın begin()
ve üzerinde yinelemeniz dışında 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
, vb
C ++ 17 ile , aralık tabanlı bir for döngüsü içinde yapılandırılmış bir bağlama kullanabilirsiniz ( John H.'nin yanıtını buna göre uyarlayarak ):
#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';
}
}
Maalesef, C ++ 17 standardı, value
kullanmıyor olsanız bile değişkeni bildirmenizi gerektirir ( std::ignore
bir tanesi std::tie(..)
işe yaramaz, bu tartışmaya bakın ).
Bazı derleyiciler bu nedenle kullanılmayan value
değişken hakkında sizi uyarabilir ! Kullanılmayan değişkenlerle ilgili derleme zamanı uyarıları, aklımdaki herhangi bir üretim kodu için uygun değildir. Bu nedenle, bu belirli derleyici sürümleri için geçerli olmayabilir.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Ian'ın bahsettiği daha genel şablonlu çözümün altında ...
#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; }
};
Tüm krediler Ian'a gidiyor ... Teşekkürler Ian.
İşte Boost'un transform_iterator kullanarak nasıl yapılacağına dair bir örnek
#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;
}
}
Açık begin
ve end
gerekli olmadığında, yani aralık döngüsü için, anahtarlar üzerinden döngü (ilk örnek) veya değerler (ikinci örnek) ile elde edilebilir
#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;
Bunu yapmak ister misin?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
Yalnızca anahtarları döndüren bir yineleyiciye ihtiyacınız varsa, haritanın yineleyicisini istenen arabirimi sağlayan kendi sınıfınızda sarmalamanız gerekir. Mevcut yardımcı yapıları kullanarak, burada olduğu gibi sıfırdan yeni bir yineleyici sınıfı bildirebilirsiniz . Bu cevap , transform_iterator
yineleyiciyi yalnızca değerleri / anahtarları döndüren bir tanesine sarmak için Boost'un nasıl kullanılacağını gösterir .
Bu yanıt BOOST_FOREACH
,. Bunun yerine c ++ 'ın aralığı tabanlı kullanabilirsiniz.
#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;
}
}
Boost olmadan bunu bu şekilde yapabilirsiniz. GetKeyIterator () yerine bir cast operatörü yazabilseydiniz güzel olurdu, ama onu derleyemiyorum.
#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());
}
}
Gelecek nesil için ve bir aralık oluşturmanın bir yolunu bulmaya çalıştığım için, bir alternatif boost :: adapters :: transform kullanmaktır
İşte küçük bir örnek:
#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;
}
Değerler üzerinde yineleme yapmak istiyorsanız, lambda'da kullanın t.second
.
Burada birçok iyi cevap var, aşağıda bunları yazmanıza izin veren birkaçını kullanan bir yaklaşım var:
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
Her zaman istediğin buysa, işte MapKeys () için kod:
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);
}
Ian'ın tüm harita türleriyle çalışmak için yanıtını benimsedim ve bir referans döndürmeyi düzelttim 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;
}
};
Bunun sorunuzu yanıtlamadığını biliyorum, ancak bakmak isteyebileceğiniz bir seçenek, aynı indeksi "bağlantılı" bilgi olan iki vektöre sahip olmaktır ..
Yani ...
std::vector<std::string> vName;
std::vector<int> vNameCount;
adların isme göre sayılmasını istiyorsanız, sadece for döngüsünü vName.size () üzerinden yaparsınız ve bulduğunuzda, aradığınız vNameCount dizini budur.
Elbette bu size haritanın tüm işlevlerini vermeyebilir ve bağlı olarak daha iyi olabilir veya olmayabilir, ancak anahtarları bilmiyorsanız daha kolay olabilir ve çok fazla işlem yapmamalısınız.
Sadece birinden ne zaman ekleyip sildiğinizi hatırlayın, bunu diğerinden yapmanız gerekir, yoksa işler çıldırır heh: P