Bagaimana saya bisa mengulang peta peta C ++?

292

Bagaimana saya bisa loop std::mapdalam C ++? Peta saya didefinisikan sebagai:

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

Misalnya, wadah di atas menampung data seperti ini:

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

Bagaimana saya bisa mengulang peta ini dan mengakses berbagai nilai?

Mendongkrak
sumber
25
Anda mungkin mempertimbangkan menerima jawaban Riot untuk c ++ modern, melakukannya untuk para googler.
Sergio Basurco
Tidak sepenuhnya yakin bahwa memiliki peta peta akan menjadi contoh Minimal, Lengkap, dan dapat diverifikasi tetapi intinya dibuat!
davidhood2
3
Jika Anda melewatkan pemberitahuan, izinkan saya mengulangi komentar chuckleplant: Anda dapat mempertimbangkan menerima jawaban Riot untuk c ++ modern, lakukan untuk para googler.
noɥʇʎԀʎzɐɹƆ
Jawaban Puppy lebih fleksibel, tetapi jika dilihat dari jumlah upvotes, googler menginginkan jawaban Riot lebih banyak.
Legion Daeth

Jawaban:

563

Pertanyaan lama tetapi jawaban yang tersisa sudah usang pada C ++ 11 - Anda dapat menggunakan rentang berbasis untuk loop dan cukup lakukan:

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

ini harus jauh lebih bersih daripada versi sebelumnya, dan menghindari salinan yang tidak perlu.

Beberapa bantuan mengganti komentar dengan definisi eksplisit dari variabel referensi (yang bisa dioptimalkan jika tidak digunakan):

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;
  }
}
Kerusuhan
sumber
13
Alat peraga untuk menjaga jawaban yang relevan - Saya hanya berharap ini bisa naik lebih dekat ke atas. Mungkin mengedit ini untuk jawaban yang diterima akan sesuai? (Itu yang kami lakukan di TeX.SX, tapi SO adalah budaya yang berbeda.)
Sean Allred
2
Hanya pertanyaan singkat, adakah relevansi dengan keputusan Anda menulis constsetelah auto? Apakah ini murni estetika?
Parham
6
@Parham const sebelum atau setelah tipe yang ditentukan adalah masalah preferensi, tapi saya memilih untuk tetap di sebelah kanan karena membuatnya lebih jelas dalam situasi di mana pointer sedang digunakan; misalnya ketika menggunakan keduanya int const *xdan int *const xAnda dapat menuliskannya sebagai int const *const xIMO yang jauh lebih jelas daripada const int *const x. Tapi itu hanya diurai dari kiri ke kanan sehingga efeknya sama. Lihat jawaban atas pertanyaan ini: stackoverflow.com/questions/5503352/const-before-or-const-after
Riot
2
apa maksud & dalam auto const & ent2?
Tanner Summers
5
@ TannerSummers karena mengakses dengan nilai akan menambah inefisiensi menyalin setiap elemen; selain itu jika Anda ingin memodifikasi konten, Anda harus mengakses elemen dengan referensi (atau petunjuk) daripada berdasarkan nilai.
Kerusuhan
308

Anda bisa menggunakan iterator.

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.
}
Anak anjing
sumber
10
Kecuali jika dia ingin memodifikasi peta, menggunakan const_iterator akan lebih baik.
Michael Aaron Safyan
28
itu lebih efisien untuk melakukan ++ iterator daripada iterator ++ karena menghindari salinan yang tidak perlu ketika bertambah.
Game_Overture
19
Menggunakan otomatis sangat menyederhanakan loop untuk C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard
127
Ini cukup usang untuk c ++ 11. Cukup gunakan untuk (auto iter: mymap)
Entitas Anonim
37
Untuk c ++ 11, Anda harus menggunakan (auto & iter: mymap) untuk menghindari salinan potensial.
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;
    }
}

atau lebih bagus di 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
sumber
2
Anda harus menggunakan auto &, atau jika Anda tidak mengubah peta, bahkan const & auto. Selain itu, lebih suka non-anggota begin () dan end (), yaitu untuk (const auto & iter = begin (peta); ...).
Ela782
13
Atau bahkan lebih sederhana: untuk (const auto & element: map) cout << element.second;
Ela782
26

Dengan C ++ 17 (atau lebih baru), Anda dapat menggunakan fitur "binding terstruktur", yang memungkinkan Anda menentukan banyak variabel, dengan nama yang berbeda, menggunakan satu tupel / pasangan. Contoh:

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

The proposal asli (oleh tokoh-tokoh Bjarne Stroustrup, Herb Sutter dan Gabriel Dos Reis) adalah menyenangkan untuk membaca (dan sintaks yang disarankan adalah lebih intuitif IMHO); ada juga kata-kata yang diusulkan untuk standar yang membosankan untuk dibaca tetapi lebih dekat dengan apa yang sebenarnya masuk.

einpoklum
sumber
2
Ini sangat cantik saya harus memilih meskipun C ++ 17 belum "ada" belum. Sobat, mereka benar-benar merevitalisasi C ++ dengan membuatnya lebih mudah untuk menulis kode yang bersih dan aman.
Jonas
24

Lakukan sesuatu seperti ini:

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
sumber
Di detik untuk itu harus ++ ii bukan ++ i :)
Slipstream
Saya pikir '/ n' harus menjadi '\ n' pada akhirnya
Kenyakorn Ketsombut
Yah saya akan menggunakan definisi untuk undef mereka nanti tetapi ini adalah cara yang baik untuk 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;

keluaran:

name1:value1:data1
name1:value2:data2
name2:value1:data1
name2:value2:data2
name3:value1:data1
name3:value2:data2
pengguna1438233
sumber
2
Bagaimana jawaban ini berbeda dari stackoverflow.com/a/27344958/3658660 ? Kecuali fakta bahwa itu membuat salinan di mana-mana.
hlscalon
1

gunakan std::map< std::string, std::map<std::string, std::string> >::const_iteratorsaat peta adalah const.

Amir Saniyan
sumber
1
Anda tahu, kadang-kadang bukan kebiasaan yang baik untuk menyembunyikan kode di belakang margin yang tepat. Saya mengerti ini lebih aman tetapi juga benar-benar mengaburkan visi kode. Pergi autobro, atau dia yang menggunakan vim akan pergi KO.
Ludovic Zenohate Lagouardette
0

Seperti einpoklum disebutkan dalam jawaban mereka , karena C ++ 17 Anda juga dapat menggunakan deklarasi penjilidan terstruktur . Saya ingin memperluas itu dengan memberikan solusi lengkap untuk beralih di atas peta peta dengan cara yang nyaman:

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

Catatan 1: Untuk mengisi peta, saya menggunakan daftar penginisialisasi (yang merupakan fitur C ++ 11 ). Ini kadang-kadang berguna untuk menjaga inisialisasi tetap tetap ringkas.

Catatan 2: Jika Anda ingin memodifikasi peta mdi dalam loop, Anda harus menghapus constkata kunci.

Kode di Coliru

membunyikan
sumber
0

Solusi pertama adalah Gunakan range_based untuk loop, seperti:

Catatan: Kapan range_expressiontipe std::mapkemudian range_declarationjenis a std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

Solusi Kedua:

Kode 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
sumber