ฉันจะวนซ้ำแผนที่ C ++ ของแผนที่ได้อย่างไร


292

ฉันจะวนซ้ำstd::mapใน C ++ ได้อย่างไร แผนที่ของฉันถูกกำหนดเป็น:

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

ตัวอย่างเช่นคอนเทนเนอร์ด้านบนเก็บข้อมูลดังนี้:

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

ฉันจะวนลูปผ่านแผนที่นี้และเข้าถึงค่าต่างๆได้อย่างไร?


25
คุณอาจพิจารณายอมรับคำตอบของ Riot สำหรับ c ++ สมัยใหม่ทำเพื่อชาว Google
Sergio Basurco

ไม่แน่ใจทั้งหมดที่มีแผนที่แผนที่จะเป็นตัวอย่างที่น้อยที่สุดสมบูรณ์ตรวจสอบได้แต่มีจุดเกิดขึ้น!
davidhood2

3
ในกรณีที่คุณไม่ได้รับการแจ้งเตือนให้ฉันทำซ้ำความคิดเห็นของ chuckleplant: คุณอาจพิจารณายอมรับคำตอบของ Riot สำหรับ c ++ ที่ทันสมัยทำเพื่อ googler
noɥʇʎԀʎzɐɹƆ

คำตอบของลูกสุนัขนั้นมีความหลากหลายมากกว่า แต่การตัดสินจากจำนวน upvotes ที่ googler ต้องการคำตอบของ Riot มากกว่า
กองพัน Daeth

คำตอบ:


563

คำถามเก่า แต่คำตอบที่เหลือนั้นล้าสมัยตั้งแต่ C ++ 11 - คุณสามารถใช้ranged ตามลูปและทำได้:

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

สิ่งนี้ควรจะสะอาดกว่าเวอร์ชั่นก่อนหน้าและหลีกเลี่ยงการทำสำเนาที่ไม่จำเป็น

ความโปรดปรานบางส่วนแทนที่ความคิดเห็นด้วยคำจำกัดความที่ชัดเจนของตัวแปรอ้างอิง (ซึ่งได้รับการปรับให้เหมาะสมหากไม่ได้ใช้):

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
อุปกรณ์ประกอบฉากสำหรับการรักษาคำตอบที่เกี่ยวข้อง - ฉันเพียง แต่หวังว่าสิ่งนี้จะเพิ่มขึ้นเรื่อย ๆ บางทีการแก้ไขในคำตอบที่ยอมรับนั้นเหมาะสมหรือไม่ (มันคือสิ่งที่เราทำกับ TeX.SX แต่ SO เป็นวัฒนธรรมที่แตกต่างกัน)
Sean Allred

2
แค่คำถามสั้น ๆ มีความเกี่ยวข้องกับการตัดสินใจของคุณในการเขียนconstหลังจากauto? มันเป็นความงามอย่างหมดจดหรือไม่?
Parham

6
@Parham const ก่อนหรือหลังประเภทที่ระบุเป็นเรื่องของการตั้งค่า แต่ฉันเลือกที่จะเก็บไว้ทางด้านขวาเพราะมันทำให้ชัดเจนในสถานการณ์ที่มีการใช้พอยน์เตอร์ ตัวอย่างเช่นเมื่อใช้ทั้งสองint const *xและint *const xคุณสามารถเขียนเป็นซึ่งเป็นที่ชัดเจนมากขึ้นกว่าint const *const x IMO const int *const xแต่แยกวิเคราะห์จากซ้ายไปขวาดังนั้นเอฟเฟกต์จึงเหมือนกัน ดูคำตอบสำหรับคำถามนี้: stackoverflow.com/questions/5503352/const-before-or-const-after
Riot

2
& หมายถึงอะไรใน const auto & ent2
แทนเนอร์ซัมเมอร์ส

5
@TannerSummers เนื่องจากการเข้าถึงโดยใช้ค่าจะเพิ่มประสิทธิภาพในการคัดลอกแต่ละองค์ประกอบ นอกจากนี้หากคุณต้องการแก้ไขเนื้อหาคุณจะต้องเข้าถึงองค์ประกอบโดยการอ้างอิง (หรือตัวชี้) แทนที่จะตามค่า
Riot

308

คุณสามารถใช้ตัววนซ้ำ

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
นอกเสียจากเขาตั้งใจที่จะแก้ไขแผนที่การใช้ const_iterator จะดีกว่า
Michael Aaron Safyan

28
มีประสิทธิภาพมากกว่าในการทำ ++ iterator มากกว่า iterator ++ เนื่องจากจะหลีกเลี่ยงการคัดลอกที่ไม่จำเป็นเมื่อทำการเพิ่ม
Game_Overture

19
การใช้อัตโนมัติช่วยลดความซับซ้อนของลูปสำหรับ C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
เจอราร์ด

127
นี่ค่อนข้างล้าสมัยสำหรับ c ++ 11 เพียงใช้สำหรับ (auto iter: mymap)
นิติบุคคลที่ไม่ระบุตัวตน

37
สำหรับ c ++ 11 คุณควรใช้ (auto & iter: mymap) เพื่อหลีกเลี่ยงการคัดลอกที่อาจเกิดขึ้น
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;
    }
}

หรือดีกว่าใน 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
คุณควรใช้ auto & หรือถ้าคุณไม่แก้ไขแผนที่แม้แต่ const auto & นอกจากนี้ให้เลือกผู้ที่ไม่ได้เป็นสมาชิก start () และ end () เช่น (const auto & iter = start (map); ... )
Ela782

13
หรือง่ายยิ่งขึ้น: สำหรับ (อัตโนมัติ const & องค์ประกอบ: แผนที่) ศาล << element.second;
Ela782

26

ด้วย C ++ 17 (หรือใหม่กว่า) คุณสามารถใช้คุณสมบัติ "การผูกโครงสร้าง" ซึ่งช่วยให้คุณกำหนดตัวแปรหลายตัวด้วยชื่อที่แตกต่างกันโดยใช้ tuple / pair เดียว ตัวอย่าง:

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

ข้อเสนอเดิม (โดยผู้ทรงคุณวุฒิ Bjarne Stroustrup สมุนไพร Sutter และกาเบรียลดอสเรอิส) คือความสนุกในการอ่าน (และไวยากรณ์ปัญหาคือใช้งานง่ายมากขึ้น IMHO); นอกจากนี้ยังมีถ้อยคำที่เสนอสำหรับมาตรฐานที่น่าเบื่อในการอ่าน แต่ใกล้เคียงกับสิ่งที่จะเข้ามาจริง


2
มันสวยมากฉันต้องลงคะแนนทั้งๆที่ C ++ 17 ยังไม่ได้ "อยู่ที่" เลย มนุษย์พวกเขากำลังฟื้นฟู C ++ จริง ๆ โดยทำให้ง่ายต่อการเขียนรหัสที่สะอาดและปลอดภัย
Jonas

24

ทำสิ่งนี้:

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

ในวินาทีสำหรับมันควรเป็น ++ ii ไม่ใช่ ++ i :)
Slipstream

ฉันคิดว่า '/ n' ควรเป็น '\ n' ในที่สุด
Kenyakorn Ketsombut

ดีฉันจะได้ใช้กำหนดเพื่อ undef พวกเขาในภายหลังหนทางนี้เป็นวิธีที่ดีสำหรับ 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;

เอาท์พุท:

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

2
คำตอบนี้แตกต่างจากstackoverflow.com/a/27344958/3658660อย่างไร ยกเว้นการทำสำเนาทุกที่
hlscalon

1

ใช้std::map< std::string, std::map<std::string, std::string> >::const_iteratorเมื่อแผนที่เป็น const


1
คุณรู้ไหมว่าบางครั้งมันก็เป็นนิสัยที่ดีที่จะซ่อนโค้ดไว้ด้านหลังระยะขอบด้านขวา ฉันเข้าใจว่ามันปลอดภัยกว่า แต่ก็ชัดเจนว่าการมองเห็นของโค้ดนั้นไม่สมบูรณ์ ไปหาเพื่อนautoหรือคนที่ใช้เสียงเรียกเข้าก็จะเป็น KO
Ludovic Zenohate Lagouardette

0

ในฐานะที่เป็นeinpoklumกล่าวถึงในคำตอบของพวกเขาตั้งแต่C ++ 17คุณยังสามารถใช้โครงสร้างการประกาศผลผูกพัน ฉันต้องการอธิบายเพิ่มเติมโดยนำเสนอโซลูชันที่สมบูรณ์สำหรับการวนซ้ำแผนที่ของแผนที่อย่างสะดวกสบาย:

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

หมายเหตุ 1:สำหรับการเติมแผนที่ฉันใช้รายการ initializer (ซึ่งเป็นคุณสมบัติC ++ 11 ) บางครั้งการทำเช่นนี้อาจช่วยให้การกำหนดค่าเริ่มต้นคงที่กะทัดรัด

หมายเหตุ 2:หากคุณต้องการแก้ไขแผนที่mภายในลูปคุณต้องลบconstคำหลักออก

รหัสเกี่ยวกับ Coliru


0

วิธีแก้ปัญหาแรกคือใช้ range_based สำหรับลูปเช่น:

หมายเหตุ: เมื่อrange_expression'ชนิดคือstd::mapแล้วrange_declaration' s std::pairประเภทคือ

for ( range_declaration : range_expression )      
  //loop_statement

รหัส 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
   }
}

ทางออกที่สอง:

รหัส 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 
     }
 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.