如何遍历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员工做。
塞尔吉奥·巴苏尔科

不能完全确定具有地图的地图将是“ 最小,完整,可验证”的示例,但重点是!
davidhood2 '16

3
万一您错过了通知,让我重复一下chuckleplant的评论:您可能会考虑接受Riot对于现代c ++的回答,请为Google员工做。
noɥʇʎԀʎzɐɹƆ

Puppy的答案更具通用性,但从Google希望Riot答案更多的投票数来看。
军团再临

Answers:


563

旧问题,但其余答案从C ++ 11开始已过时-您可以使用基于范围的for循环,只需执行以下操作:

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是一种不同的文化。)
肖恩·奥尔雷德

2
只是一个简单的问题,您const之后的写作决定是否与您相关auto?它纯粹是美学的吗?
帕勒姆(Parham)2014年

6
在指定类型之前或之后的@Parham const是一个优先事项,但我选择将其保留在右边,因为在使用指针的情况下它使它更清晰;例如,当同时使用两者时int const *xint *const x您可以将其编写为int const *const x比IMO更清晰的IMO const int *const x。但是它只是从左到右解析,因此效果是相同的。请参阅以下问题的答案:stackoverflow.com/questions/5503352/const-before-or-const-after
Riot 2014年

2
auto const&ent2中的&是什么意思?
Tanner Summers

5
@TannerSummers,因为按值访问会增加复制每个元素的效率;另外,如果要修改内容,则需要通过引用(或指针)而不是值来访问元素。
2016年

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 2011年

28
与迭代器相比,执行++ iterator效率更高,因为它避免了增量时不必要的复制。
Game_Overture 2013年

19
使用auto大大简化了C ++ 11的循环:for(auto iterator = m.begin(); iterator != m.end(); iterator++)
Gerard 2014年

127
对于c ++ 11来说,这已经过时了。只需用于(auto iter:mymap)
匿名实体

37
对于c ++ 11,应使用(auto&iter:mymap)避免可能的复制。
dev_nut14年

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&。另外,更喜欢非成员begin()和end(),即for(const auto&iter = begin(map); ...)。
Ela782 2014年

13
甚至更简单:for(const auto&element:map)cout << element.second;
Ela782 2014年

26

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

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

原来的建议(由灯具Bjarne的Stroustrup的,香草萨特和Gabriel杜斯雷斯)是有趣的阅读(和建议的语法更直观恕我直言); 该标准还有一些建议的措词,虽然无聊,但更接近实际内容。


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

好吧,我本来会用定义来定义它们,然后再对它们进行定义,这对于C ++ 98来说是个好方法:) +1
Ludovic Zenohate Lagouardette 16/02/12

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 '16


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:为填充地图,我使用了一个初始化列表(这是C ++ 11的功能)。有时可以方便地使固定的初始化紧凑。

注意2:如果要m在循环中修改地图,则必须删除const关键字。

关于Coliru的代码


0

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

注意:当range_expression“S型是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 
     }
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.