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

Как я могу перебрать std::map в C ++? Моя карта определяется как:

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

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


person Jack    schedule 30.01.2011    source источник


Ответы (9)


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

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;
  }
}
person Riot    schedule 07.12.2014
comment
Реквизит для того, чтобы ответы были актуальными - я только хотел бы, чтобы это могло приблизиться к вершине. Возможно, будет уместно отредактировать это в принятом ответе? (Это то, что мы делаем в TeX.SX, но SO - это другая культура.) - person Sean Allred; 12.12.2014
comment
Просто быстрый вопрос, имеет ли отношение к вашему решению писать const после auto? Это чисто эстетично? - person Parham; 30.12.2014
comment
@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 - person Riot; 30.12.2014
comment
что означает & в автоконстант & ent2? - person Tanner Summers; 09.04.2016
comment
@TannerSummers - это доступ к каждому члену по константной ссылке, а не по значению. - person Riot; 09.04.2016
comment
@Riot, можешь объяснить, зачем это нужно? - person Tanner Summers; 09.04.2016
comment
@TannerSummers, потому что доступ по значению добавит неэффективности копирования каждого элемента; кроме того, если вы хотите изменить содержимое, вам нужно будет обращаться к элементам по ссылкам (или указателям), а не по значению. - person Riot; 10.04.2016
comment
@Riot Это действительно полезная техническая особенность, которую я никогда раньше не слышал (в тот год, когда я занимался c ++) - если вы передадите какой-либо объект-член как объект и получит ли он доступ к оригиналу, а если вы передадите объект, он получит доступ к точной копии .. .? - person davidhood2; 02.11.2016
comment
@ davidhood2 передает что-либо без & для ссылки или * для указателя, делает копию, да. - person Riot; 02.11.2016
comment
Также обратите внимание на этот синтаксис for (auto&& [first,second] : mymap) здесь - person Brent Bradburn; 26.02.2018

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

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.
}
person Puppy    schedule 30.01.2011
comment
это прекрасно работает, но при выполнении cout ‹< it_type- ›first ‹< endl; дает мне ошибку, ожидаемую до первичного выражения - ›токен - person Jack; 30.01.2011
comment
Это потому, что it_type - это тип, а iterator - это переменная. Виноват. - person Puppy; 30.01.2011
comment
Ах, не беспокойтесь. Я должен был это заметить. Спасибо, в любом случае - person Jack; 30.01.2011
comment
Если он не намеревается изменять карту, лучше использовать const_iterator. - person Michael Aaron Safyan; 30.01.2011
comment
использовать итератор ++ более эффективно, чем использовать итератор ++, поскольку он позволяет избежать ненужной копии при увеличении. - person Game_Overture; 11.10.2013
comment
Использование auto значительно упрощает цикл для C ++ 11: for(auto iterator = m.begin(); iterator != m.end(); iterator++) - person Gerard; 30.04.2014
comment
Это довольно устарело для C ++ 11. Просто используйте для (auto iter: mymap) - person Anonymous Entity; 23.05.2014
comment
@Balk нет никакой разницы в производительности. Если с компилятором что-то не так, код сборки будет идентичным. - person quant; 13.07.2014
comment
Для c ++ 11 вы должны использовать (auto & iter: mymap), чтобы избежать потенциальной копии. - person dev_nut; 16.11.2014
comment
@ user11177 docs Я вижу, что в выражении range_expression эта переменная auto будет иметь тип *iterator и сама не будет итератором? - person Alnitak; 09.12.2014
comment
@ user11177: Если вы это сделаете, вы получите пару, а не итератор, так что это должно быть for (auto& entry : mymap) - person Claudiu; 14.07.2015
comment
@quant возможно, но зачем рисковать, когда это совершенно не нужно и семантически мешает? тот факт, что почти все, кажется, по умолчанию используют постинкремент / декремент, является ужасной привычкой, которая бессмысленна, если только пре-временное не требуется для чего-то. Я полагаю, что вина за эту распространенную тенденцию лежит в ужасном решении назвать язык C ++ вместо ++ C - как будто C был улучшен, но лучшая версия была выброшена, и вместо этого мы получили копию оригинала, хех. - person underscore_d; 18.12.2015

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;
    }
}
person Axel Gneiting    schedule 30.01.2011
comment
Вы должны использовать auto &, или, если вы не изменяете карту, даже const auto &. Кроме того, предпочтительнее использовать функции begin () и end (), не являющиеся членами, то есть for (const auto & iter = begin (map); ...). - person Ela782; 05.04.2014
comment
Или еще проще: for (const auto & element: map) cout ‹< element.second; - person Ela782; 05.04.2014

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

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

оригинальное предложение (от корифеев Bjarne Stroustrup, Herb Sutter и Gabriel Dos Reis) интересно читать (а предлагаемый синтаксис более интуитивно понятен, IMHO); есть также предлагаемая формулировка стандарта который скучно читать, но он ближе к тому, что на самом деле нужно.

person einpoklum    schedule 02.09.2016
comment
Это так красиво, что мне нужно проголосовать, несмотря на то, что C ++ 17 еще не появился. Черт, они действительно возрождают C ++, облегчая написание чистого и безопасного кода. - person Jonas; 07.02.2017

Сделайте что-нибудь вроде этого:

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';
    }
}   
person Community    schedule 30.01.2011
comment
Во втором для него должно быть ++ ii, а не ++ i :) - person Slipstream; 06.12.2013
comment
Я думаю, что '/ n' должно быть '\ n' в конце - person Kenyakorn Ketsombut; 28.07.2014
comment
Что ж, я бы использовал определения, чтобы потом их восстановить, но это хороший способ для C ++ 98 :) +1 - person Ludovic Zenohate Lagouardette; 12.02.2016

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
person user1438233    schedule 03.04.2015
comment
Чем этот ответ отличается от stackoverflow.com/a/27344958/3658660? За исключением того факта, что он везде делает копии. - person hlscalon; 28.11.2016

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

person Amir Saniyan    schedule 25.03.2015
comment
Знаете, иногда прятать код за правым полем - плохая привычка. Я понимаю, это безопаснее, но ну иль полностью размывает видение кода. Иди auto братан, или тот, кто использует vim, пойдет в нокаут. - person Ludovic Zenohate Lagouardette; 12.02.2016

Как упоминалось в 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;
}

Выход:

m [имя1] [значение1] = данные1
m [имя1] [значение2] = данные2
m [имя2] [значение1] = данные1
m [имя2] [значение2] = данные2
m [имя3] [значение1] = данные1
m [имя3] [значение2] = данные2

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

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

Код на Coliru

person honk    schedule 01.01.2019

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

Примечание. Если тип range_expression std::map, то тип range_declaration - 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 
     }
 }
person AmirSalar    schedule 06.12.2019