web-dev-qa-db-fra.com

Comment puis-je parcourir une carte de cartes C++?

Comment puis-je parcourir un std::map en C++? Ma carte est définie comme:

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

Par exemple, le conteneur ci-dessus contient des données telles que:

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

Comment puis-je parcourir cette carte et accéder aux différentes valeurs?

279
Jack

Ancienne question mais les réponses restantes sont obsolètes à partir de C++ 11 - vous pouvez utiliser un basé sur pour la boucle et simplement faire:

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
  }
}

cela devrait être beaucoup plus propre que les versions précédentes et éviter les copies inutiles. 

Certains préfèrent remplacer les commentaires par des définitions explicites de variables de référence (qui sont optimisées si elles ne sont pas utilisées):

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;
  }
}
546
Riot

Vous pouvez utiliser un itérateur.

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.
}
305
Puppy
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;
    }
}

ou plus agréable en 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;
    }
}
58
Axel Gneiting

Faites quelque chose comme ça:

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';
    }
}   
23
user283145

En C++ 17, vous pourrez utiliser la fonctionnalité "liaisons structurées", qui vous permet de définir plusieurs variables, avec des noms différents, à l'aide d'un seul tuple/paire. Exemple:

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

La proposition originale (par les luminaires Bjarne Stroustrup, Herb Sutter et Gabriel Dos Reis) est amusante à lire (et la syntaxe suggérée est plus intuitive à mon humble avis); il y a aussi le libellé proposé pour la norme qui est ennuyeux à lire mais qui est plus proche de ce qui va réellement se passer.

22
einpoklum

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;

sortie:

name1:value1:data1
name1:value2:data2
name2:value1:data1
name2:value2:data2
name3:value1:data1
name3:value2:data2
11
user1438233

utilisez std::map< std::string, std::map<std::string, std::string> >::const_iterator lorsque map est const.

1
Amir Saniyan

Comme einpoklum mentionné dans leur réponse , puisque C++ 17 vous pouvez également utiliser des déclarations de liaison structurées . Je souhaite approfondir ce sujet en fournissant une solution complète permettant de parcourir facilement une carte de cartes:

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

Note 1: Pour remplir la carte, j'ai utilisé un initializer list (qui est un C++ 11 feature). Cela peut parfois être utile pour garder les initialisations fixes compactes.

Note 2: Si vous souhaitez modifier la carte m au sein des boucles, vous devez supprimer les mots clés const.

Code sur Coliru

0
honk