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?


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

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

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

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.


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

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

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.


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


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 
     }
 }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.