Come posso scorrere una mappa di mappe C ++?


292

Come posso fare il ciclo in a std::mapin C ++? La mia mappa è definita come:

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

Ad esempio, il contenitore sopra contiene dati come questo:

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

Come posso scorrere questa mappa e accedere ai vari valori?


25
Potresti considerare di accettare la risposta di Riot per il c ++ moderno, fallo per i googler.
Sergio Basurco,

Non del tutto sicuro che avere una mappa di mappe sarebbe un esempio minimo, completo, verificabile, ma il punto è fatto!
davidhood2,

3
Nel caso in cui tu abbia perso la notifica, lasciami ripetere il commento di Chuckleplant: potresti considerare di accettare la risposta di Riot per il c ++ moderno, fallo per i googler.
noɥʇʎԀʎzɐɹƆ

La risposta di Puppy è più versatile, ma a giudicare dal numero di voti che i googler vogliono di più dalla risposta di Riot.
Legion Daeth,

Risposte:


563

Vecchia domanda ma le risposte rimanenti sono obsolete a partire da C ++ 11: puoi usare un intervallo basato su loop per fare semplicemente:

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

questo dovrebbe essere molto più pulito rispetto alle versioni precedenti ed evitare copie inutili.

Alcuni preferiscono sostituire i commenti con definizioni esplicite delle variabili di riferimento (che vengono ottimizzate se non utilizzate):

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
Puntelli per mantenere le risposte pertinenti - Vorrei solo che questo potesse avvicinarsi alla cima. Forse la modifica di questo nella risposta accettata sarebbe appropriata? (È quello che facciamo su TeX.SX, ma SO è una cultura diversa.)
Sean Allred,

2
Solo una domanda veloce, c'è qualche rilevanza per la tua decisione di scrivere constdopo auto? È puramente estetico?
Parham,

6
@Parham const prima o dopo un tipo specificato è una questione di preferenza, ma ho scelto di tenerlo sulla destra perché lo rende più chiaro nelle situazioni in cui vengono utilizzati i puntatori; ad esempio quando si usano entrambi int const *xe int *const xsi può scrivere come int const *const xIMO molto più chiaro di const int *const x. Ma è appena analizzato da sinistra a destra, quindi l'effetto è lo stesso. Vedi le risposte a questa domanda: stackoverflow.com/questions/5503352/const-before-or-const-after
Riot

2
cosa significa & significa in auto const & ent2?
Tanner Summers,

5
@TannerSummers perché l'accesso per valore aggiungerebbe l'inefficienza della copia di ogni elemento; inoltre, se si desidera modificare i contenuti, è necessario accedere agli elementi mediante riferimenti (o puntatori) anziché per valore.
Riot

308

Puoi usare un iteratore.

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
A meno che non intenda modificare la mappa, sarebbe meglio usare const_iterator.
Michael Aaron Safyan,

28
è più efficiente fare ++ iterator di iterator ++ poiché evita una copia non necessaria durante l'incremento.
Game_Overture

19
L'uso di auto semplifica notevolmente il ciclo per C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard

127
Questo è piuttosto obsoleto per c ++ 11. Basta usare per (iter automatico: mymap)
Entità anonima

37
Per c ++ 11, dovresti usare (auto & iter: mymap) per evitare la potenziale copia.
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;
    }
}

o più bello 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
Dovresti usare auto &, o se non modifichi la mappa, anche const auto &. Inoltre, preferisci il non membro begin () e end (), ovvero for (const auto & iter = begin (map); ...).
Ela782

13
O ancora più semplice: for (const auto & element: map) cout << element.second;
Ela782

26

Con C ++ 17 (o versioni successive), è possibile utilizzare la funzione "associazioni strutturate", che consente di definire più variabili, con nomi diversi, utilizzando una singola tupla / coppia. Esempio:

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

La proposta originale (dei luminari Bjarne Stroustrup, Herb Sutter e Gabriel Dos Reis) è divertente da leggere (e la sintassi suggerita è IMHO più intuitiva); c'è anche la formulazione proposta per lo standard che è noioso da leggere ma è più vicino a ciò che effettivamente entrerà.


2
È così carino che devo votare nonostante C ++ 17 non sia ancora "lì". Amico, stanno davvero rivitalizzando il C ++ facilitando la scrittura di codice pulito e sicuro.
Jonas,

24

Fai qualcosa del genere:

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

Nel secondo dovrebbe essere ++ ii non ++ i :)
Slipstream il

Penso che il '/ n' dovrebbe essere un '\ n' alla fine
Kenyakorn Ketsombut,

Beh, avrei usato le definizioni per annullarle in seguito, ma questo è un buon modo per 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;

produzione:

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

2
In che modo questa risposta è diversa da stackoverflow.com/a/27344958/3658660 ? Tranne il fatto che sta facendo copie ovunque.
hlscalon,

1

utilizzare std::map< std::string, std::map<std::string, std::string> >::const_iteratorquando la mappa è const.


1
Sai, a volte non è una buona abitudine nascondere il codice dietro il margine giusto. Capisco che è più sicuro, ma beh, offusco completamente la visione del codice. Vai autofratello, o chi usa Vim andrà KO.
Ludovic Zenohate Lagouardette,

0

Come menzionato einpoklum nella loro risposta , dal C ++ 17 puoi anche usare dichiarazioni di rilegatura strutturate . Voglio estenderlo fornendo una soluzione completa per iterare su una mappa di mappe in modo comodo:

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

Nota 1: per riempire la mappa, ho usato un elenco di inizializzatori (che è una funzionalità C ++ 11 ). Questo a volte può essere utile per mantenere compatte le inizializzazioni fisse.

Nota 2: se si desidera modificare la mappa mall'interno dei loop, è necessario rimuovere le constparole chiave.

Codice su Coliru


0

La prima soluzione è Usa range_based per loop, come:

Nota: quando range_expressionil tipo è, std::mapallora è un range_declarationtipo std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

La seconda soluzione:

Codice 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 
     }
 }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.