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.