【问题标题】:How can I loop through a C++ map of maps?如何循环遍历地图的 C++ 地图?
【发布时间】:2011-06-18 05:02:06
【问题描述】:

如何在 C++ 中循环遍历 std::map?我的地图定义为:

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

如何循环遍历此地图并访问各种值?

【问题讨论】:

    标签: c++ loops dictionary iteration idioms


    【解决方案1】:

    您可以使用迭代器。

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

    【讨论】:

    • 除非他打算修改地图,否则使用 const_iterator 会更好。
    • 使用 ++iterator 比使用 iterator++ 更有效,因为它在递增时避免了不必要的复制。
    • 使用 auto 大大简化了 C++11 的循环:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
    • 这对于 c++11 来说已经过时了。只需使用 for (auto iter : mymap)
    • 对于 c++11,你应该使用 (auto& iter : mymap) 来避免潜在的复制。
    【解决方案2】:
    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;
        }
    }
    

    【讨论】:

    • 你应该使用auto&,或者如果你不修改地图,即使是const auto&。另外,更喜欢非成员的 begin() 和 end(),即 for(const auto& iter = begin(map); ...)。
    • 或者更简单:for(const auto& element : map) cout
    【解决方案3】:

    做这样的事情:

    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 :)
    • 我认为'/n'最后应该是'\n'
    • 好吧,我以后会使用定义来取消定义它们,这是 C++98 的好方法 :) +1
    【解决方案4】:

    老问题,但其余答案自 C++11 起已过时 - 您可以使用 ranged based for loop 并简单地执行以下操作:

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

    这应该比早期版本更干净,并避免不必要的复制。

    有些人喜欢用引用变量的显式定义替换 cmets(如果未使用,这些变量会被优化掉):

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

    【讨论】:

    • 保持答案相关性的建议——我只希望这能越来越接近顶部。也许将其编辑为已接受的答案是合适的? (这是我们在 TeX.SX 上所做的,但 SO 是一种不同的文化。)
    • 问个简单的问题,你决定在auto 之后写const 有什么关系吗?纯粹是审美吗?
    • @Parham const 在指定类型之前或之后是一个偏好问题,但我选择将其保留在右侧,因为它在使用指针的情况下更清晰;例如,当同时使用int const *xint *const x 时,您可以将其写为int const *const x,这比const int *const x 更清晰IMO。但它只是从左到右解析,所以效果是一样的。查看此问题的答案:stackoverflow.com/questions/5503352/const-before-or-const-after
    • auto const &ent2 中的 & 是什么意思?
    • @TannerSummers 因为按值访问会增加复制每个元素的效率;另外,如果您想修改内容,则需要通过引用(或指针)而不是值来访问元素。
    【解决方案5】:

    当 map 为 const 时使用 std::map&lt; std::string, std::map&lt;std::string, std::string&gt; &gt;::const_iterator

    【讨论】:

    • 你知道,有时候把代码隐藏在右边距后面并不是一个好习惯。我知道它更安全,但完全模糊了代码的视野。去autobro,不然用vim的人会被KO。
    【解决方案6】:

    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
    

    【讨论】:

    【解决方案7】:

    使用 C++17(或更高版本),您可以使用“结构化绑定”功能,该功能允许您使用单个元组/对定义多个具有不同名称的变量。示例:

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

    original proposal(由杰出人物 Bjarne Stroustrup、Herb Sutter 和 Gabriel Dos Reis 撰写)读起来很有趣(恕我直言,建议的语法更直观);还有proposed wording for the standard,虽然读起来很无聊,但更接近实际内容。

    【讨论】:

    • 这太漂亮了,我需要投票,尽管 C++17 还没有“存在”。伙计,他们通过让编写干净和安全的代码变得更容易,真正振兴了 C++。
    【解决方案8】:

    正如their answer 中提到的einpoklum,因为C++17 你也可以使用structured binding declarations。我想通过提供一个完整的解决方案来扩展这一点,以一种舒适的方式迭代地图:

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

    输出:

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

    注 1: 为了填充地图,我使用了 initializer list(这是 C++11 功能)。这有时可以方便地保持固定初始化的紧凑性。

    注意 2:如果你想在循环中修改映射 m,你必须删除 const 关键字。

    Code on Coliru

    【讨论】:

      【解决方案9】:

      第一个解决方案是使用 range_based for 循环,例如:

      注意:当range_expression 的类型为std::map 时,range_declaration 的类型为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 
           }
       }
      

      【讨论】:

        猜你喜欢
        • 2014-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-19
        • 1970-01-01
        相关资源
        最近更新 更多