Як я можу переглядати карти 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 ++, зробіть це для гуглерів.
Серхіо Басурко

Не зовсім впевнений, що мати карту буде прикладом мінімального, повного, перевіреного, але справа зроблена!
davidhood2

3
Якщо ви пропустили сповіщення, дозвольте повторити коментар chuckleplant: Ви можете розглянути можливість прийняття відповіді Riot за сучасний c ++, зробіть це для googlers.
noɥʇʎԀʎzɐɹƆ

Відповідь цуценя є більш універсальною, але, судячи з кількості обґрунтувань, гуглери хочуть відповіді Ріота більше.
Легіон Даєт

Відповіді:


563

Старе запитання, але решта відповідей застаріла на рівні 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;
  }
}

13
Реквізити для відповідності відповідей - я лише хочу, щоб це могло піднятися ближче до вершини. Можливо, редагування цього у прийнятій відповіді було б доречним? (Це те, що ми робимо на TeX.SX, але ТАК це інша культура.)
Шон Олред

2
Лише швидке запитання, чи є якесь значення для вашого рішення constпісля написання auto? Це чисто естетично?
Пархам

6
@Parham const до або після вказаного типу - це питання переваги, але я вирішу тримати його праворуч, оскільки це робить зрозумілішим у ситуаціях, коли використовуються покажчики; наприклад, при використанні обох, int const *xі int *const xви можете записати це як int const *const xнабагато зрозуміліше ІМО, ніж const int *const x. Але це просто проаналізовано зліва направо, щоб ефект був таким самим. Дивіться відповіді на це запитання: stackoverflow.com/questions/5503352/const-before-or-const-after
бунт

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
Використання авто значно спрощує цикл для C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Джерард

127
Це досить застаріло для c ++ 11. Просто використовуйте для (auto iter: mymap)
Анонімний суб’єкт

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
Ви повинні використовувати автоматичне &, або якщо ви не змінюєте карту, навіть const auto &. Крім того, віддайте перевагу не членам start () та end (), тобто для (const auto & iter = begin (карта); ...).
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) цікаво читати (і пропонований синтаксис більш інтуїтивним ІМХО); також є запропоноване формулювання стандарту, який нудно читати, але ближче до того, що насправді буде входити.


2
Це так симпатично, що мені потрібно проголосувати, незважаючи на те, що C ++ 17 ще не "там". Людина, вони дійсно пожвавлюють C ++, полегшуючи писати чистий і безпечний код.
Йонас

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

2
Чим ця відповідь відрізняється від stackoverflow.com/a/27344958/3658660 ? За винятком того, що він робить копії скрізь.
hlscalon

1

використовувати, std::map< std::string, std::map<std::string, std::string> >::const_iteratorколи карта const.


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 для циклу, наприклад:

Примітка: Коли 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 
     }
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.