Wie kann ich eine C ++ - Karte mit Karten durchlaufen?

292

Wie kann ich eine std::mapin C ++ durchlaufen ? Meine Karte ist definiert als:

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

Der obige Container enthält beispielsweise Daten wie folgt:

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

Wie kann ich diese Karte durchlaufen und auf die verschiedenen Werte zugreifen?

Jack
quelle
25
Sie könnten erwägen, Riots Antwort für modernes C ++ zu akzeptieren, und dies für die Googler tun.
Sergio Basurco
Ich bin mir nicht ganz sicher, ob eine Karte mit Karten ein minimales, vollständiges und überprüfbares Beispiel wäre, aber der Punkt ist klar !
Davidhood2
3
Falls Sie die Benachrichtigung verpasst haben, lassen Sie mich den Kommentar von chuckleplant wiederholen: Sie könnten in Betracht ziehen,
noɥʇʎԀʎzɐɹƆ
Die Antwort von Puppy ist vielseitiger, aber gemessen an der Anzahl der positiven Stimmen wollen die Googler die Antwort von Riot mehr.
Legion Daeth

Antworten:

563

Alte Frage, aber die restlichen Antworten sind ab C ++ 11 veraltet. Sie können eine for-basierte for-Schleife verwenden und einfach Folgendes tun:

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

Dies sollte viel sauberer sein als die früheren Versionen und vermeidet unnötige Kopien.

Einige bevorzugen es, die Kommentare durch explizite Definitionen von Referenzvariablen zu ersetzen (die bei Nichtverwendung optimiert werden):

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;
  }
}
Randalieren
quelle
13
Requisiten, um die Antworten relevant zu halten - ich wünschte nur, dies könnte sich näher an die Spitze erheben. Vielleicht wäre es angebracht, dies in die akzeptierte Antwort zu ändern? (Es ist das, was wir auf TeX.SX machen, aber SO ist eine andere Kultur.)
Sean Allred
2
Nur eine kurze Frage, gibt es eine Relevanz für Ihre Entscheidung des Schreibens constnach auto? Ist es rein ästhetisch?
Parham
6
@Parham const vor oder nach einem angegebenen Typ ist eine Frage der Präferenz, aber ich entscheide mich dafür, ihn rechts zu halten, da er in Situationen, in denen Zeiger verwendet werden, klarer wird. Zum Beispiel, wenn Sie beide verwenden int const *xund int *const xSie können es so schreiben, dass es int const *const xIMO viel klarer ist als const int *const x. Aber es wird nur von links nach rechts analysiert, damit der Effekt der gleiche ist. Siehe die Antworten auf diese Frage: stackoverflow.com/questions/5503352/const-before-or-const-after
Riot
2
Was bedeutet das & in der Auto-Konstante & ent2?
Tanner Summers
5
@TannerSummers, da der Zugriff nach Wert die Ineffizienz des Kopierens jedes Elements erhöhen würde; Wenn Sie den Inhalt ändern möchten, müssen Sie außerdem über Verweise (oder Zeiger) und nicht über Werte auf die Elemente zugreifen.
Aufstand
308

Sie können einen Iterator verwenden.

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.
}
Hündchen
quelle
10
Wenn er nicht beabsichtigt, die Karte zu ändern, ist die Verwendung von const_iterator besser.
Michael Aaron Safyan
28
++ Iterator ist effizienter als Iterator ++, da beim Inkrementieren unnötige Kopien vermieden werden.
Game_Overture
19
Die Verwendung von Auto vereinfacht die Schleife für C ++ 11 erheblich:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard
127
Dies ist für c ++ 11 ziemlich veraltet. Verwenden Sie einfach für (Auto-Iter: Mymap)
Anonyme Entität
37
Für c ++ 11 sollten Sie (auto & iter: mymap) verwenden, um die mögliche Kopie zu vermeiden.
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;
    }
}

oder schöner in 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;
    }
}
Axel Gneiting
quelle
2
Sie sollten auto & verwenden, oder, wenn Sie die Karte nicht ändern, sogar const auto &. Bevorzugen Sie außerdem das Nichtmitglied begin () und end (), dh für (const auto & iter = begin (map); ...).
Ela782
13
Oder noch einfacher: für (const auto & element: map) cout << element.second;
Ela782
26

Mit C ++ 17 (oder höher) können Sie die Funktion "Strukturierte Bindungen" verwenden, mit der Sie mehrere Variablen mit unterschiedlichen Namen mit einem einzigen Tupel / Paar definieren können. Beispiel:

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

Der ursprüngliche Vorschlag (von den Größen Bjarne Stroustrup, Herb Sutter und Gabriel Dos Reis) macht Spaß zu lesen (und die vorgeschlagene Syntax ist meiner Meinung nach intuitiver); Es gibt auch den vorgeschlagenen Wortlaut für den Standard der langweilig zu lesen ist, aber näher an dem liegt, was tatsächlich hineingehen wird.

einpoklum
quelle
2
Das ist so hübsch, dass ich abstimmen muss, obwohl C ++ 17 noch nicht "da" ist. Mann, sie revitalisieren C ++ wirklich, indem sie es einfacher machen, sauberen und sicheren Code zu schreiben.
Jonas
24

Mach so etwas:

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';
    }
}   
Kevin Reid
quelle
In der zweiten sollte es ++ ii nicht ++ i sein :)
Slipstream
Ich denke, das '/ n' sollte am Ende ein '\ n' sein
Kenyakorn Ketsombut
Nun, ich hätte Definitionen verwendet, um sie später aufzuheben, aber dies ist ein guter Weg für 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;

Ausgabe:

name1:value1:data1
name1:value2:data2
name2:value1:data1
name2:value2:data2
name3:value1:data1
name3:value2:data2
user1438233
quelle
2
Wie unterscheidet sich diese Antwort von stackoverflow.com/a/27344958/3658660 ? Bis auf die Tatsache, dass es überall Kopien macht.
Hlscalon
1

verwenden, std::map< std::string, std::map<std::string, std::string> >::const_iteratorwenn map const ist.

Amir Saniyan
quelle
1
Wissen Sie, es ist manchmal keine gute Angewohnheit, Code hinter dem rechten Rand zu verstecken. Ich verstehe, dass es sicherer ist, aber ich verwische die Vision des Codes vollständig. Geh autoBruder, oder wer Vim benutzt, wird KO.
Ludovic Zenohate Lagouardette
0

Wie einpoklum in erwähnt ihre Antwort , da C ++ 17 können Sie auch Erklärungen verbindlich strukturiert . Ich möchte das erweitern, indem ich eine vollständige Lösung für das bequeme Durchlaufen einer Kartenkarte bereitstelle:

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

Hinweis 1: Zum Ausfüllen der Karte habe ich eine Initialisierungsliste verwendet (eine C ++ 11- Funktion). Dies kann manchmal nützlich sein, um feste Initialisierungen kompakt zu halten.

Hinweis 2: Wenn Sie die Karte minnerhalb der Schleifen ändern möchten , müssen Sie die constSchlüsselwörter entfernen .

Code auf Coliru

hupen
quelle
0

Die erste Lösung lautet Verwenden Sie range_based for loop wie:

Hinweis: Wenn range_expression‚s - Typ ist std::mapdann ein range_declaration‘ s - Typ ist std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

Die zweite Lösung:

Code 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
quelle