Чи є спосіб перебрати ключі, а не пари карти C ++?
Чи є спосіб перебрати ключі, а не пари карти C ++?
Відповіді:
Якщо вам дійсно потрібно приховати значення, яке повертає "справжній" ітератор (наприклад, тому, що ви хочете використовувати свій ітератор ключів зі стандартними алгоритмами, щоб вони працювали над клавішами замість пар), тоді подивіться на Boost's transform_iterator .
[Порада: переглядаючи документацію Boost для нового класу, спочатку прочитайте "приклади". Тоді ви маєте спортивний шанс зрозуміти, про що йдеться на землі :-)]
map - асоціативний контейнер. Отже, ітератор - це пара ключа, val. Якщо вам потрібні лише ключі, ви можете ігнорувати частину значень з пари.
for(std::map<Key,Val>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{
Key k = iter->first;
//ignore value
//Value v = iter->second;
}
РЕДАКТУВАННЯ: Якщо ви хочете виставити назовні лише ключі, ви можете перетворити карту у векторні або клавіші та експонувати.
const Key& k(iter->first);
std::vector<Key> v(myMap.begin(), myMap.end())
.
З 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";
}
}
Це можна зробити, просто продовживши ітератор STL для цієї карти. Наприклад, відображення рядків у ints:
#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());
template<typename C> class key_iterator : public C::iterator
тощо
З C ++ 17 ви можете використовувати структуровану прив'язку всередині діапазону на основі циклу (відповідно адаптувавши відповідь Джона Х. ):
#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
змінну! Попередження під час компіляції щодо невикористаних змінних на мою думку не підлягають жодному виробничому коду. Отже, це може не застосовуватися для певних версій компілятора.
for ([[maybe_unused]] const auto &[key, v_not_used] : my_map) { use(key); }
Нижче більш загальне шаблонне рішення, на яке Іан посилався ...
#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; }
};
Усі кредити йдуть Іану ... Дякую Іану.
Ось приклад того, як це зробити за допомогою трансформатора 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;
}
}
Якщо явного 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, 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;
}
}
Без підвищення можна зробити це так. Було б добре, якби ви могли написати оператор лиття замість 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
лямбда.
Тут багато хороших відповідей, нижче - підхід із використанням декількох з них, який дозволяє вам написати це:
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;
}
};
Я знаю, що це не дає відповіді на ваше запитання, але один із варіантів, який ви можете поглянути, - це лише наявність двох векторів з тим самим індексом, який "пов'язаний" інформацією.
Так у ..
std::vector<std::string> vName;
std::vector<int> vNameCount;
якщо ви хочете кількість імен за іменем, ви просто зробите швидкий пошук циклу на vName.size (), і коли ви знайдете, це індекс для vNameCount, який ви шукаєте.
Звичайно, це може не надавати вам всієї функціональності карти, і залежно від цього може бути, а може і не бути кращим, але це може бути простіше, якщо ви не знаєте ключів і не повинні додавати занадто багато обробки.
Просто пам’ятайте, коли ви додаєте / видаляєте з одного, ви повинні зробити це з іншого, інакше речі стануть шаленими хе: P