C ++マップでキーを反復する


121

C ++マップのペアではなく、キーを反復処理する方法はありますか?


イテレータを値に到達させるアイデアは、STLアルゴリズムで使用することです(たとえば、2つのマップのキーの交差)。Boostを含むソリューションは、Boostイテレータを生成するため、これを許可しません。最悪の回答が最も多くの票を獲得します!

回答:


70

「実際の」イテレータが返す値を本当に非表示にする必要がある場合(たとえば、キーイテレータを標準アルゴリズムで使用して、ペアではなくキーを操作するため)、Boostのtransform_iterator

[ヒント:新しいクラスのBoostドキュメントを見るときは、最初に最後にある「例」を読んでください。それからあなたはそれが残りの何について話しているのかを地球上で理解するスポーツのチャンスを持っています:-)]


2
{何かやる} |(ブースト::アダプタ:: map_keys constのkey_tのキー、the_map)ブーストを使用すると、BOOST_FOREACHを書くことができますboost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/...
rodrigobを

120

マップは連想コンテナです。したがって、イテレータは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;
}

編集::キーのみを外部に公開する場合は、マップをベクトルまたはキーに変換して公開できます。


しかし、ベクトルのイテレータを外部に公開するのは本当に悪い考えです。
ナビーン

イテレータを公開しないでください。ベクトルでキーを提供するだけ
aJ。

5
あなたは代わりに、これを行うことができます: const Key& k(iter->first);
strickli

17
2つは、これはOPの質問に正確に答えて、彼がすでに知っていて探していなかった答えで答えるということです。次に、この方法は、次のようなことをしたい場合には役に立ちませんstd::vector<Key> v(myMap.begin(), myMap.end())
Andreas Magnusson 2014年

キーをベクトルに変換しないでください。新しいベクトルを作成すると、反復処理の目的が損なわれます。反復処理は高速であり、何も割り当てられないことになっています。また、大きなセットの場合は遅くなります。
Kevin Chen

84

C ++ 11では、反復構文は単純です。ペアを繰り返し処理しますが、キーだけにアクセスするのは簡単です。

#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";
    }
}

29
元の質問は「ペアではない」と明示的に言っています。
Ian

41

ブーストなし

そのためには、そのマップの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());

16
+1:最後に、「ペアではない」ビットを読んだ人!乾杯、これでスペックを掘り下げる時間を節約できました!
マークKコーワン2014

1
そして、テンプレート化されたソリューションの下に、Valueイテレーターを追加しました。
degski 2016

私の質問にリンクしました。
Ian

template<typename C> class key_iterator : public C::iterator、など
ガブリエル

38

C ++ 17を使用すると、範囲ベースのforループ内で構造化バインディングを使用できますJohn 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変数について警告する場合があります。未使用の変数に関するコンパイル時の警告は、私の心の中でどんな生産コードにとっても行き止まりです。そのため、これは特定のコンパイラバージョンに適用できない場合があります。


あなたはそれをstd :: ignoreに原則的に割り当てることができませんでしたか?それは実際にコンパイルされたコードの効率を損なうでしょうか、それとも実際には何も評価しませんか?(私はバインディングで意味するのではなく、ループ内のアクションとして)
KotoroShinoto

C ++ 17以降では、[[maybe_unused]]も使用できます。これは警告を抑制します。このように:for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
アルワコ

15

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; }
};

すべてのクレジットはイアンに送られます...イアンに感謝します。


11

あなたはmap_keysを探しています、それであなたは次のようなものを書くことができます

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

1
BOOST_FOREACH(const key_t& key, ...
strickli 2013

5

これは、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;
  }
}

4

明示的beginendはなく、必要な場合、つまり範囲ループの場合、キー(最初の例)または値(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;

1
stdにある必要があります
Mordachai 2018

3

あなたはこれをしたいですか?

std::map<type,type>::iterator iter = myMap.begin();
std::map<type,type>::iterator iter = myMap.end();
for(; iter != endIter; ++iter)
{
   type key = iter->first;  
   .....
}

はい、わかっています。問題は、クラスAを持っていることです{public://ここにプライベートマップのキーのイテレータを公開したいprivate:map <>};
ボグダンバラン

その場合、std :: trasnformを使用してマップからキーのみを取得することにより、std :: listを作成できると思います。次に、リストに要素を追加しても既存のイテレータが無効にならないため、リストイテレータを公開できます。
ナビーン

3

キーを返すだけのイテレータが必要な場合は、目的のインターフェースを提供する独自のクラスでマップのイテレータをラップする必要があります。ここのよう、既存のヘルパー構造を使用して、新しいイテレータクラスを最初から宣言できます。この答えは、Boostを使用してtransform_iterator、値/キーのみを返すイテレータをラップする方法を示しています。


2

あなたは出来る

  • カスタムイテレータクラスを作成し、 std::map<K,V>::iterator
  • 使用std::transformあなたのmap.begin()map.end()boost::bind( &pair::second, _1 )ファンクタ
  • ループで->second反復している間は、メンバーを無視してforください。

2

この回答は、がないことを除いて、rodrigobの回答に似ていBOOST_FOREACHます。代わりにに基づいて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;
     }
}

0

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());
    }
}

0

後世のために、そして私が範囲を作成する方法を見つけようとしていたので、代替は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は、ラムダで使用します。


0

ここにたくさんの良い答えがあります。以下は、これを書くためのいくつかの方法を使用したアプローチです。

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);
}

0

私はすべてのマップタイプで動作するように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;
    }
};

-1

私はこれであなたの質問に答えられないことを知っていますが、あなたが見たいと思うかもしれない一つのオプションは、「リンクされた」情報である同じインデックスを持つ2つのベクトルを持つことです。

だから…

std::vector<std::string> vName;

std::vector<int> vNameCount;

名前ごとの名前のカウントが必要な場合は、vName.size()に対してforループをすばやく実行します。これが見つかったら、探しているvNameCountのインデックスです。

確かに、これはマップのすべての機能を提供するわけではなく、依存する方が良い場合もそうでない場合もありますが、キーがわからない場合や、あまり多くの処理を追加しない方が簡単な場合があります。

片方から追加/削除するときはもう片方から行う必要があることを忘れないでください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.