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?


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

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

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

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.


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

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

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.


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


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 
     }
 }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.