Итерация ключей в карте C++

Есть ли способ перебирать ключи, а не пары карт C++?

20 ответов

Решение

Если вам действительно нужно скрыть значение, которое возвращает "реальный" итератор (например, потому что вы хотите использовать итератор ключей со стандартными алгоритмами, чтобы они работали с ключами вместо пар), то взгляните на Boost's transform_iterator.

[Подсказка: при просмотре документации Boost для нового класса, сначала прочтите "примеры" в конце. Тогда у вас есть спортивный шанс выяснить, о чем, на самом деле, говорят остальные:-)]

Карта является ассоциативным контейнером. Следовательно, итератор - это пара ключей,val. Если вам нужны только ключи, вы можете игнорировать часть значения из пары.

for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k =  iter->first;
//ignore value
//Value v = iter->second;
}

РЕДАКТИРОВАТЬ:: Если вы хотите выставить только ключи снаружи, то вы можете преобразовать карту в вектор или ключи и выставить.

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

В C++17 вы можете использовать структурированное связывание внутри цикла for на основе диапазона (соответственно адаптируя ответ Джона Х.):

#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 переменная! Предупреждения во время компиляции относительно неиспользуемых переменных не нужны для любого производственного кода в моей голове. Таким образом, это может быть неприменимо для определенных версий компилятора.

Без повышения

Вы можете сделать это, просто расширив итератор 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());

Ниже более общее шаблонное решение, на которое ссылался Ян...

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

Все кредиты идут в Ян... Спасибо, Ян.

С C ++ 20 у нас есть доступ к <tcode id="40762257"></tcode> библиотека, в которой есть отличное решение для этого: <tcode id="40762258"></tcode>

      #include <ranges>

//...

std::map<int, int> myMap = {{1,2},{3,4},{5,6}};
auto keys = std::views::keys(myMap);
for(auto key : keys) {
    std::cout << key << std::endl;
}

Попробуйте сами: https://godbolt.org/z/heeWv4Gh6

Вы ищете map_keys, с его помощью вы можете написать такие вещи, как

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

Без BOOST, напрямую используя ключ и значение

for(auto const& [key, value]: m_map)
    {
    std::cout<<" key="<<key;
    std::cout<<" value="<<value<<std::endl;
    }

Когда нет явного begin а также end необходимо, т. е. для циклического выбора диапазона, циклические ключи (первый пример) или значения (второй пример) могут быть получены с помощью

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

Вы хотите сделать это?

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

Если вам нужен итератор, который просто возвращает ключи, вам нужно обернуть итератор карты в свой собственный класс, предоставляющий желаемый интерфейс. Вы можете объявить новый класс итератора с нуля, как здесь, для использования существующих вспомогательных конструкций. Этот ответ показывает, как использовать Boost's transform_iterator обернуть итератор в тот, который возвращает только значения / ключи.

Этот ответ похож на Родригоба, кроме как без 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;
     }
}

Вы могли бы

  • создать собственный класс итератора, агрегируя std::map<K,V>::iterator
  • использование std::transform вашей map.begin() в map.end() с boost::bind( &pair::second, _1 ) функтор
  • просто игнорируй ->second член во время итерации с for петля.

Я знаю, что это не отвечает на ваш вопрос, но один вариант, на который вы, возможно, захотите взглянуть, - это просто наличие двух векторов с одним и тем же индексом как "связанной" информации

Так что в..

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

std::vector<int> vNameCount;

если вам нужно количество имен по имени, вы просто делаете быстрый цикл for по vName.size(), и когда вы найдете его, это индекс для vNameCount, который вы ищете.

Конечно, это может не дать вам всей функциональности карты, а зависимость может быть лучше, а может и не быть лучше, но может быть проще, если вы не знаете ключей и не должны добавлять слишком много обработки.

Просто помните, когда вы добавляете / удаляете из одного, вы должны делать это из другого, иначе все сойдет с ума, хе:P

Здесь много хороших ответов, ниже приведен подход, использующий пару из них, который позволяет написать это:

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

Я принял ответ Яна для работы со всеми типами карт и исправил возвращение ссылки для 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;
    }
};

Без 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::adapters::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 в лямбде.

Другие вопросы по тегам