C ++ 맵에서 키 반복


121

C ++ 맵 쌍이 아닌 키를 반복하는 방법이 있습니까?


반복자를 값으로 가져 오는 아이디어는 STL 알고리즘에서 사용하는 것입니다 (예 : 두 맵의 키 교차). Boost와 관련된 솔루션은 Boost 반복자를 생성하기 때문에 이것을 허용하지 않습니다. 최악의 답변이 가장 많은 표를 얻습니다!

답변:


70

"실제"반복기가 반환하는 값을 정말로 숨겨야하는 경우 (예를 들어 표준 알고리즘과 함께 키 반복기를 사용하여 쌍 대신 키에서 작동하도록하려는 경우) Boost의 transform_iterator .

[팁 : 새 클래스에 대한 Boost 문서를 볼 때 먼저 끝에있는 "예제"를 읽으십시오. 그런 다음 나머지 부분이 무엇에 대해 이야기하고 있는지 알아낼 스포츠 기회가 있습니다. :-)]


2
boost를 사용하면 BOOST_FOREACH (const key_t key, the_map | boost :: adaptors :: map_keys) {do something} boost.org/doc/libs/1_50_0/libs/range/doc/html/range/reference/…
rodrigob를 작성할 수 있습니다.

120

지도는 연관 컨테이너입니다. 따라서 iterator는 한 쌍의 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;
}

편집 : : 외부에 키만 노출하려는 경우지도를 벡터 또는 키로 변환하고 노출 할 수 있습니다.


그러나 벡터의 반복자를 외부에 노출시키는 것은 정말 나쁜 생각입니다.
Naveen

반복자를 노출하지 마십시오. 벡터에 키를 제공하십시오
aJ.

5
대신이 작업을 수행 할 수 있습니다. const Key& k(iter->first);
strickli

17
두 가지, 이것은 OP의 질문에 그가 이미 알고 있고 찾지 못한 정확한 대답으로 대답합니다. 두 번째 로이 방법은 다음과 같은 작업을 수행하려는 경우 도움이되지 않습니다 std::vector<Key> v(myMap.begin(), myMap.end()).
Andreas Magnusson 2014 년

키를 벡터로 변환하지 마십시오. 새로운 벡터를 만드는 것은 속도가 빠르고 아무것도 할당하지 않는 반복의 목적을 무효화합니다. 또한 큰 세트의 경우 속도가 느려집니다.
Kevin Chen

85

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 반복기를 확장하면됩니다. 예를 들어 문자열을 정수로 매핑하는 경우 :

#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 : 마지막으로 "쌍이 아닙니다"비트를 읽는 사람! 건배, 이것은 사양을 파헤치는 시간을 절약했습니다!
Mark K Cowan

1
그리고 템플릿 솔루션 아래에 Value iterator를 추가했습니다.
degski

내 질문을 연결했습니다.
Ian

template<typename C> class key_iterator : public C::iterator
Gabriel

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에 할당 할 수 없습니까? 실제로 컴파일 된 코드의 효율성을 떨어 뜨리거나 실제로 아무것도 평가하지 않을까요? (I 바인딩이 아니라 루프 내에서 조치로 의미하지 않는다)
KotoroShinoto

C ++ 17부터 [[maybe_unused]]를 사용할 수도 있습니다. 이것은 경고를 억제합니다. 다음과 같이 :for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
arhuaco

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

모든 크레딧은 Ian에게 ... 고마워 Ian.



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

명확한 경우 begin와는 end필요하지 않다, 즉,이 범위의 루핑을위한 키 (제 1 예) 또는 값 (예를 들어 초)에 걸쳐 루프를 얻을 수있다

#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
표준에 있어야합니다
Mordachai

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 <>}; 프라이빗 맵의 키에 반복자를 노출하고 싶습니다.
Bogdan Balan

이 경우 std :: trasnform을 사용하고 맵에서 키만 선택하여 std :: list를 만들 수 있다고 생각합니다. 그런 다음 목록에 더 많은 요소를 삽입해도 기존 반복기가 무효화되지 않으므로 목록 반복기를 노출 할 수 있습니다.
Naveen

3

키만 반환하는 이터레이터가 필요한 경우 원하는 인터페이스를 제공하는 자체 클래스에서 맵의 이터레이터를 래핑해야합니다. 기존 헬퍼 구성을 사용하여 여기 와 같이 처음부터 새 반복기 클래스를 선언 할 수 있습니다 . 이 답변 은 Boost를 사용 transform_iterator하여 값 / 키 만 반환하는 반복기를 래핑하는 방법을 보여줍니다 .


2

당신은 할 수 있습니다

  • 사용자 지정 반복기 클래스를 만들고 std::map<K,V>::iterator
  • 사용 std::transform당신의 map.begin()map.end() A를 boost::bind( &pair::second, _1 )
  • 루프로 ->second반복하는 동안 멤버를 무시하십시오 for.

2

이 대답은 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

나는 이것이 당신의 질문에 대답하지 않는다는 것을 알고 있지만, 당신이보고 싶은 한 가지 옵션은 "연결된"정보가되는 동일한 인덱스를 가진 두 개의 벡터를 갖는 것입니다.

그래서 ..

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

std::vector<int> vNameCount;

이름으로 이름의 개수를 원하는 경우 vName.size ()를 통해 빠른 for 루프를 수행하고 나중에 찾을 때 찾고있는 vNameCount의 인덱스입니다.

물론 이것은 맵의 모든 기능을 제공하지 않을 수 있으며, 더 좋을 수도 있고 그렇지 않을 수도 있지만 키를 모르고 너무 많은 처리를 추가하지 않는 것이 더 쉬울 수 있습니다.

하나에서 추가 / 삭제할 때 다른 하나에서 수행해야합니다. 그렇지 않으면 일이 미쳐 버릴 것입니다 heh : P

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.