Jak mogę przejść przez mapę C ++ map?


292

Jak mogę zapętlić std::mapw C ++? Moja mapa jest zdefiniowana jako:

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

Na przykład powyższy kontener zawiera takie dane:

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

Jak mogę przejść przez tę mapę i uzyskać dostęp do różnych wartości?


25
Możesz rozważyć zaakceptowanie odpowiedzi Riot dla nowoczesnego c ++, zrób to dla googlersów.
— Sergio Basurco,

Nie jestem do końca pewien, czy posiadanie mapy map byłoby minimalnym, kompletnym, weryfikowalnym przykładem, ale o to chodzi!
— davidhood2

3
W przypadku pominięcia powiadomienia pozwól mi powtórzyć komentarz chuckleplant: Możesz rozważyć zaakceptowanie odpowiedzi Riot dla nowoczesnego c ++, zrób to dla googlersów.
— noɥʇʎԀʎzɐɹƆ

Odpowiedź szczeniaka jest bardziej wszechstronna, ale sądząc po liczbie głosów pozytywnych, pracownicy Google chcą więcej odpowiedzi Riot.
— Legion Daeth,

Odpowiedzi:


563

Stare pytanie, ale pozostałe odpowiedzi są nieaktualne od C ++ 11 - możesz użyć pętli opartej na pętli i po prostu zrobić:

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

powinno to być znacznie czystsze niż wcześniejsze wersje i pozwala uniknąć niepotrzebnych kopii.

Niektórzy opowiadają się za zastąpieniem komentarzy wyraźnymi definicjami zmiennych referencyjnych (które zostają zoptymalizowane, jeśli nie są używane):

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
Rekwizyty dla utrzymania trafności odpowiedzi - chciałbym tylko, żeby to mogło zbliżyć się do szczytu. Być może odpowiednie byłoby edytowanie tego w zaakceptowanej odpowiedzi? (To właśnie robimy na TeX.SX, ale SO to inna kultura.)
— Sean Allred

2
Krótkie pytanie, czy ma to jakiś wpływ na twoją decyzję o pisaniu constpóźniej auto? Czy to czysto estetyczne?
— Parham

6
@Parham const przed lub po określonym typie jest kwestią preferencji, ale wybieram utrzymanie go po prawej stronie, ponieważ czyni go wyraźniejszym w sytuacjach, w których używane są wskaźniki; na przykład, gdy używasz obu int const *xi int *const xmożesz napisać to, int const *const xco jest o wiele jaśniejsze niż IMO const int *const x. Ale jest tylko analizowany od lewej do prawej, więc efekt jest taki sam. Zobacz odpowiedzi na to pytanie: stackoverflow.com/questions/5503352/const-before-or-const-after
— Riot

2
co oznacza & w auto const i ent2?
— Tanner Summers

5
@TannerSummers, ponieważ dostęp przez wartość zwiększyłby nieefektywność kopiowania każdego elementu; dodatkowo, jeśli chcesz zmodyfikować zawartość, musisz uzyskać dostęp do elementów za pomocą referencji (lub wskaźników), a nie według wartości.
— Riot

308

Możesz użyć iteratora.

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
Chyba że zamierza zmodyfikować mapę, lepiej byłoby użyć const_iterator.
— Michael Aaron Safyan

28
iterator ++ jest bardziej wydajny niż iterator ++, ponieważ unika niepotrzebnej kopii podczas zwiększania.
— Game_Overture

19
Korzystanie z auto znacznie upraszcza pętlę dla C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
— Gerard

127
Jest to dość przestarzałe w przypadku c ++ 11. Wystarczy użyć dla (auto iter: mymap)
— Anonimowy

37
W przypadku c ++ 11 należy użyć (auto & iter: mymap), aby uniknąć potencjalnej kopii.
— 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;
    }
}

lub ładniej w 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
Powinieneś użyć auto & lub, jeśli nie zmodyfikujesz mapy, nawet const auto &. Ponadto preferuj nie-elementowską start () i end (), tj. For (const auto & iter = begin (map); ...).
— Ela782

13
Lub jeszcze prościej: dla (const auto & element: map) cout << element.second;
— Ela782

26

W C ++ 17 (lub nowszym) możesz użyć funkcji „powiązań strukturalnych”, która pozwala zdefiniować wiele zmiennych o różnych nazwach, za pomocą pojedynczej krotki / pary. Przykład:

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

Pierwotny wniosek (przez luminarzy Bjarne Stroustrup, Herb Sutter i Gabriel Dos Reis) jest zabawa do odczytu (i sugerowane składnia jest bardziej intuicyjny IMHO); zaproponowano również sformułowanie standardu, którego czytanie jest nudne, ale bliższe temu, co się naprawdę wydarzy.


2
To jest tak ładne, że muszę głosować, mimo że C ++ 17 jeszcze nie jest „tam”. Człowieku, naprawdę rewitalizują C ++, ułatwiając pisanie czystego i bezpiecznego kodu.
— Jonas

24

Zrób coś takiego:

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

W drugim przypadku powinno to być ++ ii nie ++ i :)
— Slipstream

Myślę, że „/ n” powinno być na końcu „\ n”
— Kenyakorn Ketsombut

Cóż,
— użyłbym

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;

wynik:

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

2
Czym różni się ta odpowiedź od stackoverflow.com/a/27344958/3658660 ? Poza tym, że wszędzie robi kopie.
— hlscalon

1

użyj, std::map< std::string, std::map<std::string, std::string> >::const_iteratorgdy mapa jest stała.


1
Wiesz, czasem nie jest dobrym nawykiem ukrywanie kodu za właściwym marginesem. Rozumiem, że jest bezpieczniejszy, ale dobrze całkowicie zamazuję wizję kodu. Idź, autobracie, albo ten, kto używa vima, przejdzie na KO.
— Ludovic Zenohate Lagouardette

0

Jak wspomniano w swojej odpowiedzi einpoklum , od C ++ 17 można również używać strukturalnych deklaracji wiążących . Chcę to rozszerzyć, zapewniając pełne rozwiązanie do iteracji mapy w wygodny sposób:

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

Uwaga 1: Do wypełnienia mapy użyłem listy inicjalizującej (która jest funkcją C ++ 11 ). Czasami może to być przydatne, aby utrzymać stałe inicjalizacje kompaktowe.

Uwaga 2: Jeśli chcesz zmodyfikować mapę mw pętli, musisz usunąć constsłowa kluczowe.

Kod na Coliru


0

Pierwszym rozwiązaniem jest użycie range_based dla pętli, np .:

Uwaga: Gdy range_expressiontypem jest, std::mapto range_declarationtypem jest std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

Drugie rozwiązanie:

Kod 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 
     }
 }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.