Haritaların C ++ haritasından nasıl geçebilirim?


292

Nasıl bir std::mapC ++ ile döngü ? Haritam şöyle tanımlanıyor:

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

Örneğin, yukarıdaki kapsayıcı şu şekilde veri tutar:

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

Bu haritada nasıl dolaşabilirim ve çeşitli değerlere nasıl erişebilirim?


25
Riot'un modern c ++ için yanıtını kabul etmeyi düşünebilirsiniz, bunu googlers için yapın.
— Sergio Basurco

Bir harita haritasının Minimal, Complete, Doğrulanabilir bir örnek olacağından tam olarak emin değilim ama nokta yapılır!
— davidhood2

3
Bildirimi kaçırmanız durumunda, chuckleplant'ın yorumunu tekrarlayayım: Riot'un modern c ++ için yanıtını kabul etmeyi düşünebilirsiniz, bunu googlers için yapın.
— noɥʇʎԀʎzɐɹƆ

Yavru köpeğin cevabı daha çok yönlüdür, ancak Google çalışanlarının upvotes sayısına göre değerlendirmek Riot'un cevabını daha fazla istemektedir.
— Lejyon Daeth

Yanıtlar:


563

Eski soru ancak kalan cevaplar C ++ 11'den beri güncel değil - döngü için aralıklı bir şekilde kullanabilirsiniz ve şunları yapabilirsiniz:

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

bu önceki sürümlerden çok daha temiz olmalı ve gereksiz kopyalardan kaçınmalıdır.

Bazıları açıklamaların referans değişkenlerin açık tanımlarıyla değiştirilmesini tercih eder (kullanılmadığında optimize edilir):

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
Yanıtları alakalı tutmak için aksesuarlar - Keşke bunun en üste yaklaşmasını isterdim. Belki de bunu kabul edilen cevapta düzenlemek uygun olur? (TeX.SX'te yaptığımız şey, ama SO farklı bir kültür.)
— Sean Allred

2
Kısa bir soru, daha constsonra yazma kararınızla herhangi bir ilgisi var automı? Tamamen estetik mi?
— Parham

6
@Parham const, belirtilen bir türden önce veya sonra bir tercih meselesidir, ancak işaretçilerin kullanıldığı durumlarda daha net hale getirdiği için sağda tutmayı tercih ederim; örneğin her ikisini de kullanırken int const *xve IMO'dan çok daha net int *const xolduğu gibi yazabilirsiniz . Ancak soldan sağa ayrıştırıldığından etki aynıdır. Bu sorunun cevaplarına bakın: stackoverflow.com/questions/5503352/const-before-or-const-afterint const *const xconst int *const x
— Riot

2
otomatik sabit & ent2 & & ne anlama geliyor?
— Tanner Summers

5
@TannerSummers çünkü değere erişim her öğeyi kopyalamanın verimsizliğini artıracaktır; ayrıca içeriği değiştirmek istiyorsanız öğelere değere göre değil referanslara (veya işaretçilerle) erişmeniz gerekir.
— Riot

308

Bir yineleyici kullanabilirsiniz.

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
Haritayı değiştirmek istemediği sürece, const_iterator kullanmak daha iyi olur.
— Michael Aaron Safyan

28
++ yineleyici yapmak yineleyici ++ 'dan daha etkilidir çünkü arttırma sırasında gereksiz bir kopyadan kaçınır.
— Game_Overture

19
Auto kullanmak C ++ 11 için döngüyü büyük ölçüde basitleştirir:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
— Gerard

127
Bu c ++ 11 için oldukça eski. Sadece kullan (otomatik iter: mymap)
— Anonim Varlık

37
C ++ 11 için, potansiyel kopyadan kaçınmak için (auto & iter: mymap) kullanmalısınız.
— 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;
    }
}

veya C ++ 0x'de daha güzel:

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
Otomatik & özelliğini kullanmanız veya haritayı değiştirmemeniz durumunda bile cons & auto &. Ek olarak, üye olmayan start () ve end () 'yi tercih edin, yani (const auto & iter = begin (map); ...).
— Ela782

13
Veya daha da basit: (const auto & element: map) cout << element.second;
— Ela782

26

C ++ 17 (veya üstü) ile, tek bir grup / çift kullanarak farklı adlara sahip birden çok değişkeni tanımlamanızı sağlayan "yapılandırılmış ciltlemeler" özelliğini kullanabilirsiniz. Misal:

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

Orijinal teklif (luminaries Bjarne Stroustrup Herb Sutter ve Gabriel Dos Reis tarafından) eğlenceli okumaktır (ve önerilen sözdizimi IMHO daha sezgisel); ayrıca, okumak için sıkıcı ancak gerçekte ne gireceğine daha yakın olan standart için önerilen ifadeler de vardır .


2
Bu çok güzel C ++ 17 henüz "orada" olmamasına rağmen oy gerekir. Dostum, temiz ve güvenli kod yazmayı kolaylaştırarak C ++ 'ı gerçekten canlandırıyorlar.
— Jonas

24

Bunun gibi bir şey yapın:

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

Bunun için ikinci olarak ++ ii değil ++ i :) olmalıdır
— Slipstream

Bence '/ n' sonunda bir '\ n' olmalı
— Kenyakorn Ketsombut

Peki, daha sonra onları tanımlamak için tanımları kullanırdım, bu C ++ 98 için iyi bir yoldur :) +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;

çıktı:

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

2
Bu cevap stackoverflow.com/a/27344958/3658660'dan nasıl farklı ? Her yerde fotokopi çekmesi dışında.
— hlscalon

1

std::map< std::string, std::map<std::string, std::string> >::const_iteratorharita sabit olduğunda kullanın .


1
Biliyorsunuz, bazen doğru marjın arkasındaki kodu gizlemek iyi bir alışkanlık değildir. Daha güvenli olduğunu anlıyorum ama kodun vizyonunu tamamen bulanıklaştırıyorum. Git autokardeşim, yoksa vim kullanan kişi KO'ya gidecek.
— Ludovic Zenohate Lagouardette

0

As einpoklum belirtilen verdikleri yanıta beri, C ++ 17 de kullanabilirsiniz bildirimleri bağlayıcı yapılandırılmış . Harita üzerinde rahat bir şekilde yineleme için tam bir çözüm sağlayarak bunu genişletmek istiyorum:

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

Not 1: Haritayı doldurmak için bir başlatıcı listesi kullandım (bu bir C ++ 11 özelliği). Bu bazen sabit başlatmaları kompakt tutmak için kullanışlı olabilir.

Not 2: Haritayı mdöngüler içinde değiştirmek istiyorsanız, haritayı döngüler içinde kaldırmanız gerekir.const anahtar kelimeleri .

Coliru Kodu


0

İlk çözüm şu döngü için range_based kullanın:

Not: Ne zaman range_expressionbireyin türüdür std::mapardından range_declaration'ın türüdür std::pair.

for ( range_declaration : range_expression )      
  //loop_statement

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

İkinci Çözüm:

Kod 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 
     }
 }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.