Как я могу просмотреть карту карт C ++?

292

Как я могу пройти через std::mapC ++? Моя карта определяется как:

std::map< std::string, std::map<std::string, std::string> >

Например, вышеприведенный контейнер содержит данные, подобные этим:

m["name1"]["value1"] = "data1";
m["name1"]["value2"] = "data2";
m["name2"]["value1"] = "data1";
m["name2"]["value2"] = "data2";
m["name3"]["value1"] = "data1";
m["name3"]["value2"] = "data2";

Как я могу пройти по этой карте и получить доступ к различным значениям?

разъем
источник
25
Вы могли бы принять ответ Riot для современного C ++, сделайте это для Google.
Серхио Басурко
Не совсем уверен, что иметь карту карт будет минимальным, полным, проверяемым примером, но суть достигнута!
davidhood2
3
Если вы пропустили уведомление, позвольте мне повторить комментарий chuckleplant: вы можете принять ответ Riot для современного c ++, сделайте это для googlers.
noɥʇʎԀʎzɐɹƆ
Ответ Puppy более универсален, но, судя по количеству голосов, гуглеры хотят получить ответ Riot больше.
Легион Дэт

Ответы:

563

Старый вопрос, но остальные ответы устарели на C ++ 11 - вы можете использовать ранжированный цикл for и просто сделать:

std::map<std::string, std::map<std::string, std::string>> mymap;

for(auto const &ent1 : mymap) {
  // ent1.first is the first key
  for(auto const &ent2 : ent1.second) {
    // ent2.first is the second key
    // ent2.second is the data
  }
}

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

Некоторые предпочитают заменять комментарии явными определениями ссылочных переменных (которые оптимизируются, если не используются):

for(auto const &ent1 : mymap) {
  auto const &outer_key = ent1.first;
  auto const &inner_map = ent1.second;
  for(auto const &ent2 : inner_map) {
    auto const &inner_key   = ent2.first;
    auto const &inner_value = ent2.second;
  }
}
буйство
источник
13
Реквизиты для поддержания актуальности ответов - я только хотел бы, чтобы это могло подняться ближе к вершине. Может быть, уместно изменить это в принятом ответе? (Это то, что мы делаем на TeX.SX, но SO это другая культура.)
Шон Оллред
2
Просто быстрый вопрос, имеет ли какое-либо отношение к вашему решению писать constпосле auto? Это чисто эстетично?
Пархем
6
@Parham const до или после указанного типа является вопросом предпочтения, но я предпочитаю держать его справа, потому что он делает его более понятным в ситуациях, когда используются указатели; например, при использовании обоих, int const *xи int *const xвы можете написать это, int const *const xчто гораздо яснее, чем IMO const int *const x. Но он просто анализируется слева направо, поэтому эффект тот же. См. Ответы на этот вопрос: stackoverflow.com/questions/5503352/const-before-or-const-after
Riot
2
что означает & в auto const & ent2?
Таннер Саммерс,
5
@TannerSummers, потому что доступ по значению добавил бы неэффективность копирования каждого элемента; Кроме того, если вы хотите изменить содержимое, вам нужно получить доступ к элементам по ссылкам (или указателям), а не по значению.
Бунт
308

Вы можете использовать итератор.

typedef std::map<std::string, std::map<std::string, std::string>>::iterator it_type;
for(it_type iterator = m.begin(); iterator != m.end(); iterator++) {
    // iterator->first = key
    // iterator->second = value
    // Repeat if you also want to iterate through the second map.
}
щенок
источник
10
Если он не намеревается изменить карту, лучше использовать const_iterator.
Майкл Аарон Сафян
28
итератор ++ эффективнее, чем итератор ++, поскольку при инкременте он избегает ненужной копии.
Game_Overture
19
Использование auto значительно упрощает цикл для C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard
127
Это довольно устарело для C ++ 11. Просто используйте для (auto iter: mymap)
Anonymous Entity
37
Для c ++ 11 вы должны использовать (auto & iter: mymap), чтобы избежать потенциальной копии.
dev_nut
60
for(std::map<std::string, std::map<std::string, std::string> >::iterator outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter) {
    for(std::map<std::string, std::string>::iterator inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) {
        std::cout << inner_iter->second << std::endl;
    }
}

или лучше в C ++ 0x:

for(auto outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter) {
    for(auto inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) {
        std::cout << inner_iter->second << std::endl;
    }
}
Аксель Гнейтинг
источник
2
Вы должны использовать auto &, или, если вы не изменяете карту, даже const auto &. Кроме того, предпочитайте non-member begin () и end (), то есть для (const auto & iter = begin (map); ...).
Ela782
13
Или даже проще: for (const auto & element: map) cout << element.second;
Ela782
26

В C ++ 17 (или более поздней версии) вы можете использовать функцию «структурированных привязок», которая позволяет вам определять несколько переменных с разными именами, используя один кортеж / пару. Пример:

for (const auto& [name, description] : planet_descriptions) {
    std::cout << "Planet " << name << ":\n" << description << "\n\n";
}

Первоначальное предложение (светилами Бьярне Страуструп, Herb Sutter и Gabriel Dos Reis) интересно читать (и предлагаемый синтаксис более интуитивным ИМХО); Есть также предложенная формулировка для стандарта, который скучно читать, но ближе к тому, что будет на самом деле.

einpoklum
источник
2
Это так мило, что мне нужно проголосовать, несмотря на то, что C ++ 17 еще не «там». Чувак, они действительно оживляют С ++, делая его проще для написания чистого и безопасного кода.
Джонас
24

Сделайте что-то вроде этого:

typedef std::map<std::string, std::string> InnerMap;
typedef std::map<std::string, InnerMap> OuterMap;

Outermap mm;

...//set the initial values

for (OuterMap::iterator i = mm.begin(); i != mm.end(); ++i) {
    InnerMap &im = i->second;
    for (InnerMap::iterator ii = im.begin(); ii != im.end(); ++ii) {
        std::cout << "map[" 
                  << i->first 
                  << "][" 
                  << ii->first 
                  << "] =" 
                  << ii->second 
                  << '\n';
    }
}   
Кевин Рид
источник
Во втором для него должно быть ++ ii, а не ++ i :)
Slipstream
Я думаю, что '/ n' должно быть в конце концов '\ n'
Kenyakorn Ketsombut
Ну, я бы использовал определения для их определения позже, но это хороший способ для C ++ 98 :) +1
Ludovic Zenohate Lagouardette
12

C ++ 11:

std::map< std::string, std::map<std::string, std::string> > m;
m["name1"]["value1"] = "data1";
m["name1"]["value2"] = "data2";
m["name2"]["value1"] = "data1";
m["name2"]["value2"] = "data2";
m["name3"]["value1"] = "data1";
m["name3"]["value2"] = "data2";

for (auto i : m)
    for (auto j : i.second)
        cout << i.first.c_str() << ":" << j.first.c_str() << ":" << j.second.c_str() << endl;

вывод:

name1:value1:data1
name1:value2:data2
name2:value1:data1
name2:value2:data2
name3:value1:data1
name3:value2:data2
user1438233
источник
2
Чем этот ответ отличается от stackoverflow.com/a/27344958/3658660 ? За исключением того, что он делает копии везде.
hlscalon
1

использовать, std::map< std::string, std::map<std::string, std::string> >::const_iteratorкогда карта постоянна

Амир Саниян
источник
1
Знаете, иногда не очень хорошая привычка прятать код за правильным полем. Я понимаю, что это безопаснее, но хорошо, что полностью стирает видение кода. Иди autoбратан, или тот, кто использует Vim, пойдет нокаутом.
Людовик Зенохате Лагуардетт
0

Как einpoklum упомянул в своем ответе , начиная с C ++ 17, вы также можете использовать объявления структурированных привязок . Я хочу расширить это, предоставляя полное решение для итерации по карте карт удобным способом:

int main() {
    std::map<std::string, std::map<std::string, std::string>> m {
        {"name1", {{"value1", "data1"}, {"value2", "data2"}}},
        {"name2", {{"value1", "data1"}, {"value2", "data2"}}},
        {"name3", {{"value1", "data1"}, {"value2", "data2"}}}
    };

    for (const auto& [k1, v1] : m)
        for (const auto& [k2, v2] : v1)
            std::cout << "m[" << k1 << "][" << k2 << "]=" << v2 << std::endl;

    return 0;
}

Примечание 1: для заполнения карты я использовал список инициализатора (который является функцией C ++ 11 ). Иногда это может быть удобно для компактности фиксированных инициализаций.

Примечание 2: если вы хотите изменить карту mвнутри циклов, вы должны удалить constключевые слова.

Код на Колиру

сигналить
источник
0

Первое решение - использовать range_based for loop, например:

Примечание: Когда range_expression«S типа std::mapзатем range_declaration» типа s это std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

Код 1:

typedef std::map<std::string, std::map<std::string, std::string>> StringToStringMap;

StringToStringMap my_map;

for(const auto &pair1 : my_map) 
{
   // Type of pair1 is std::pair<std::string, std::map<std::string, std::string>>
   // pair1.first point to std::string (first key)
   // pair1.second point to std::map<std::string, std::string> (inner map)
   for(const auto &pair2 : pair1.second) 
   {
       // pair2.first is the second(inner) key
       // pair2.second is the value
   }
}

Второе решение:

Код 2

typedef std::map<std::string, std::string> StringMap;
typedef std::map<std::string, StringMap> StringToStringMap;

StringToStringMap my_map;

for(StringToStringMap::iterator it1 = my_map.begin(); it1 != my_map.end(); it1++)
{
    // it1->first point to first key
    // it2->second point to inner map
    for(StringMap::iterator it2 = it1->second.begin(); it2 != it1->second.end(); it2++)
     {
        // it2->second point to value
        // it2->first point to second(inner) key 
     }
 }
AmirSalar
источник