Làm thế nào tôi có thể lặp qua bản đồ C ++ của bản đồ?


292

Làm thế nào tôi có thể lặp qua một std::maptrong C ++? Bản đồ của tôi được định nghĩa là:

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

Ví dụ, container ở trên chứa dữ liệu như thế này:

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

Làm thế nào tôi có thể lặp qua bản đồ này và truy cập các giá trị khác nhau?


25
Bạn có thể cân nhắc chấp nhận câu trả lời của Riot cho c ++ hiện đại, hãy làm điều đó cho các nhân viên của Google.
Sergio Basurco

Không hoàn toàn chắc chắn rằng có một bản đồ bản đồ sẽ là một ví dụ Tối thiểu, Hoàn chỉnh, Có thể kiểm chứng nhưng điểm được thực hiện!
davidhood2

3
Trong trường hợp bạn bỏ lỡ thông báo, hãy để tôi nhắc lại nhận xét của chuckleplant: Bạn có thể cân nhắc chấp nhận câu trả lời của Riot cho c ++ hiện đại, hãy làm điều đó cho các nhân viên của Google.
noɥʇʎԀʎzɐɹƆ

Câu trả lời của con chó con linh hoạt hơn, nhưng đánh giá bằng số lượng người ủng hộ thì các nhân viên của Google muốn câu trả lời của Riot nhiều hơn.
Quân đoàn Daeth

Câu trả lời:


563

Câu hỏi cũ nhưng các câu trả lời còn lại đã lỗi thời kể từ C ++ 11 - bạn có thể sử dụng một vòng lặp dựa trên vòng lặp và chỉ cần làm:

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

điều này sẽ sạch hơn nhiều so với các phiên bản trước và tránh các bản sao không cần thiết.

Một số ưu tiên thay thế các nhận xét bằng các định nghĩa rõ ràng về các biến tham chiếu (được tối ưu hóa nếu không được sử dụng):

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
Đạo cụ để giữ cho các câu trả lời có liên quan - Tôi chỉ muốn điều này có thể tăng lên gần đến đỉnh. Có lẽ chỉnh sửa điều này để trả lời được chấp nhận sẽ là phù hợp? (Đó là những gì chúng tôi làm trên TeX.SX, nhưng SO là một nền văn hóa khác.)
Sean Allred

2
Chỉ cần một câu hỏi nhanh, có bất kỳ liên quan đến quyết định viết của bạn constsau auto? Là nó hoàn toàn thẩm mỹ?
Parham

6
@Parham const trước hoặc sau một loại được chỉ định là vấn đề ưu tiên, nhưng tôi chọn giữ nó ở bên phải vì nó làm cho nó rõ ràng hơn trong các tình huống sử dụng con trỏ; ví dụ như khi sử dụng cả hai int const *xint *const xbạn có thể viết nó như int const *const xIMO rõ ràng hơn nhiều const int *const x. Nhưng nó chỉ được phân tích cú pháp từ trái sang phải nên hiệu quả là như nhau. Xem câu trả lời cho câu hỏi này: stackoverflow.com/questions/5503352/const-b
Riot

2
& có nghĩa là gì trong tự động const & ent2?
Tanner Summers

5
@TannerSummers vì truy cập theo giá trị sẽ thêm hiệu quả của việc sao chép từng phần tử; ngoài ra, nếu bạn muốn sửa đổi nội dung, bạn cần truy cập các phần tử bằng cách tham chiếu (hoặc con trỏ) thay vì theo giá trị.
Bạo loạn

308

Bạn có thể sử dụng một trình vòng lặp.

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
Trừ khi anh ta có ý định sửa đổi bản đồ, sử dụng const_iterator sẽ tốt hơn.
Michael Aaron Safyan

28
làm iterator ++ hiệu quả hơn iterator ++ vì nó tránh được một bản sao không cần thiết khi tăng.
Game_Overture

19
Sử dụng tự động đơn giản hóa rất nhiều vòng lặp cho C ++ 11:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard

127
Điều này là khá lỗi thời cho c ++ 11. Chỉ cần sử dụng cho (auto iter: mymap)
Thực thể ẩn danh

37
Đối với c ++ 11, bạn nên sử dụng (auto & iter: mymap) để tránh bản sao tiềm năng.
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;
    }
}

hoặc đẹp hơn trong 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
Bạn nên sử dụng tự động & hoặc nếu bạn không sửa đổi bản đồ, thậm chí là tự động &. Ngoài ra, thích không phải thành viên start () và end (), tức là cho (const auto & iter = started (map); ...).
Ela782

13
Hoặc thậm chí đơn giản hơn: for (const auto & Element: map) cout << Element.second;
Ela782

26

Với C ++ 17 (hoặc mới hơn), bạn có thể sử dụng tính năng "liên kết có cấu trúc", cho phép bạn xác định nhiều biến, với các tên khác nhau, bằng cách sử dụng một bộ / cặp duy nhất. Thí dụ:

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

Các đề xuất ban đầu (bằng uy tín Bjarne Stroustrup, Herb Sutter và Gabriel Dos Reis) là thú vị để đọc (và cú pháp được đề xuất là trực quan hơn IMHO); cũng có từ ngữ được đề xuất cho tiêu chuẩn nhàm chán để đọc nhưng gần hơn với những gì sẽ thực sự đi vào.


2
Điều này thật đẹp, tôi cần phải bình chọn mặc dù C ++ 17 chưa được "ở đó". Man, họ thực sự hồi sinh C ++ bằng cách viết mã sạch và an toàn dễ dàng hơn.
Jonas

24

Làm một cái gì đó như thế này:

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

Trong phần thứ hai cho nó nên ++ ii không ++ i :)
Slipstream

Tôi nghĩ rằng '/ n' cuối cùng phải là một '\ n'
Kenyakorn Ketsombut

Chà, tôi đã sử dụng định nghĩa để hoàn tác chúng sau này, đây là cách tốt cho 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;

đầu ra:

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

2
Câu trả lời này khác với stackoverflow.com/a/27344958/3658660 như thế nào? Ngoại trừ thực tế là nó tạo ra các bản sao ở khắp mọi nơi.
hlscalon

1

sử dụng std::map< std::string, std::map<std::string, std::string> >::const_iteratorkhi bản đồ là const.


1
Bạn biết đấy, đôi khi không phải là một thói quen tốt để ẩn mã đằng sau lề phải. Tôi hiểu nó an toàn hơn nhưng il hoàn toàn làm mờ tầm nhìn của mã. Đi autobro, hoặc anh nào dùng vim sẽ đi KO.
Ludovic Zenohate Lagouardette

0

Như einpoklum đã đề cập trong câu trả lời của họ , vì C ++ 17, bạn cũng có thể sử dụng các khai báo ràng buộc có cấu trúc . Tôi muốn mở rộng điều đó bằng cách cung cấp một giải pháp đầy đủ để lặp lại trên bản đồ bản đồ một cách thoải mái:

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

Lưu ý 1: Để điền vào bản đồ, tôi đã sử dụng danh sách trình khởi tạo (là tính năng C ++ 11 ). Điều này đôi khi có thể có ích để giữ cho các khởi tạo cố định nhỏ gọn.

Lưu ý 2: Nếu bạn muốn sửa đổi bản đồ mtrong các vòng lặp, bạn phải xóa các consttừ khóa.

Mã trên Coliru


0

Giải pháp đầu tiên là Sử dụng phạm vi cho vòng lặp, như:

Lưu ý: Khi range_expressionloại là std::mapthì range_declarationloại của std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

Giải pháp thứ hai:

Mã 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 
     }
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.