C++マップのペアではなく、キーを反復処理する方法はありますか?
「実際の」イテレータが返す値を非表示にする必要がある場合(たとえば、キーイテレータを標準アルゴリズムで使用して、ペアではなくキーを操作するため)、Boostを見てください。 transform_iterator 。
[ヒント:新しいクラスのBoostドキュメントを見るときは、最初に最後の「例」を読んでください。そうすれば、地球上の残りの部分が何を語っているのかを理解することができます:-)]
マップは連想コンテナです。したがって、反復子はkey、valのペアです。キーのみが必要な場合は、ペアの値部分を無視できます。
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
EDIT::キーのみを外部に公開する場合は、マップをベクトルまたはキーに変換して公開できます。
C++ 11では、反復構文は簡単です。ペアを繰り返し処理しますが、キーだけにアクセスするのは簡単です。
#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";
}
}
これを行うには、そのマップのSTLイテレーターを単純に拡張します。たとえば、文字列から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; }
};
より一般的な解決策として、 テンプレートでこの拡張を実行 することもできます。
リストのイテレータを使用するのとまったく同じようにイテレータを使用しますが、マップのbegin()
および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());
C++ 17では、 構造化バインディング を 範囲ベースのforループ 内で使用できます(適応 ジョン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';
}
}
残念ながら、C++ 17標準では、使用していない場合でもvalue
変数を宣言する必要があります( std::ignore
は std::tie(..)
を使用するため動作しません。 この説明 )を参照してください。
そのため、一部のコンパイラは、未使用のvalue
変数について警告する場合があります。未使用の変数に関するコンパイル時の警告は、私の頭の中のどの生産コードにとっても不必要です。そのため、これは特定のコンパイラバージョンには適用されない場合があります。
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; }
};
すべてのクレジットはイアンに送られます...イアンに感謝します。
map_keys を探しているので、次のように書くことができます
BOOST_FOREACH(const key_t key, the_map | boost::adaptors::map_keys)
{
// do something with key
}
明示的なbegin
およびend
が不要な場合、つまり範囲ループの場合、キー(最初の例)または値(2番目の例)のループは次のようにして取得できます。
#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;
Boostの transform_iterator を使用して行う方法の例を次に示します
#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;
}
}
キーを返すだけのイテレータが必要な場合は、目的のインターフェイスを提供する独自のクラスでマップのイテレータをラップする必要があります。既存のヘルパー構造を使用する here のように、新しいイテレータクラスをゼロから宣言できます。 この回答 は、Boostの transform_iterator
を使用して、値/キーのみを返すイテレータをラップする方法を示しています。
これをしたいですか?
std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
type key = iter->first;
.....
}
この回答は、BOOST_FOREACH
を除いてrodrigobのようなものです。代わりにc ++の範囲ベースを使用できます。
#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;
}
}
あなたは出来る
std::map<K,V>::iterator
を集約して、カスタムイテレータクラスを作成しますmap.begin()
のmap.end()
からboost::bind( &pair::second, _1 )
へのstd::transform
を使用します。for
ループでの反復中に->second
メンバーを無視するだけです。私はこれがあなたの質問に答えないことを知っていますが、あなたが見たいかもしれない1つのオプションは、「リンクされた」情報である同じインデックスを持つ2つのベクトルを持つことです。
だから.
std::vector<std::string> vName;
std::vector<int> vNameCount;
名前ごとの名前のカウントが必要な場合は、vName.size()のforループをすばやく実行し、それが見つかったら、それが探しているvNameCountのインデックスになります。
確かにこれはマップのすべての機能をyaに与えないかもしれませんし、依存する方が良いかもしれないし、そうでないかもしれませんが、yaがキーを知らないならもっと簡単かもしれません。
あなたが一方から追加/削除するとき、もう一方からそれをしなければならないことを忘れないでください、そうでないと物事が狂うようになります:P
Boostがなければ、このようにできます。 getKeyIterator()の代わりにキャスト演算子を書くことができればいいのですが、コンパイルできません。
#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());
}
}
後世のために、そして私は範囲を作成する方法を見つけようとしていたので、代替手段は boost :: adaptors :: transform を使用することです
以下に小さな例を示します。
#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;
}
値を反復処理する場合は、ラムダでt.second
を使用します。
すべてのマップタイプで動作するようにIanの回答を採用し、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;
}
};
ここで多くの良い答えがあります。以下は、それらをいくつか使用して、これを作成する方法です。
void main()
{
std::map<std::string, int> m { {"jim", 1000}, {"sally", 2000} };
for (auto key : MapKeys(m))
std::cout << key << std::endl;
}
それが常に望んでいたものである場合、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);
}