Как я могу просмотреть карту карт C ++? - PullRequest
284 голосов
/ 30 января 2011

Как я могу перебрать 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";

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

Ответы [ 8 ]

553 голосов
/ 07 декабря 2014

Старый вопрос, но остальные ответы устарели на 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;
  }
}
307 голосов
/ 30 января 2011

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

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.
}
59 голосов
/ 30 января 2011
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;
    }
}
24 голосов
/ 30 января 2011

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

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';
    }
}   
22 голосов
/ 02 сентября 2016

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

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

Оригинальное предложение (от светил Бьярне Страуструпа, Херба Саттера и Габриэля Дос Рейса) интересно читать (и предлагаемый синтаксис более интуитивно понятен IMHO);есть также предлагаемая формулировка для стандарта , которую скучно читать, но она ближе к тому, что будет на самом деле.

11 голосов
/ 03 апреля 2015

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
1 голос
/ 25 марта 2015

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

0 голосов
/ 01 января 2019

Как 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.

Код на Coliru

...