Итерация ключей в карте C ++ - PullRequest
103 голосов
/ 18 сентября 2009

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

Ответы [ 18 ]

3 голосов
/ 18 сентября 2009

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

2 голосов
/ 26 февраля 2015

Этот ответ похож на ответ Родригоба, но без 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;
     }
}
2 голосов
/ 18 сентября 2009

Вы могли бы

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

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

Так что в ..

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

std::vector<int> vNameCount;

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

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

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

0 голосов
/ 20 сентября 2018

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

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

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 голосов
/ 09 февраля 2016

Для потомков, и поскольку я пытался найти способ создания диапазона, альтернативой является использование 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 в лямбде.

0 голосов
/ 01 февраля 2013

Без 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());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...