我如何循环通过一个c++地图?

如何在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";

我如何通过这个映射循环并访问各种值?

443934 次浏览

你可以使用迭代器。

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

你可以这样做:

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

老问题,但其余的答案在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;
}
}

当map为const时使用std::map< std::string, std::map<std::string, std::string> >::const_iterator

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

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

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

最初的建议(由Bjarne Stroustrup, Herb Sutter和Gabriel Dos Reis编写)读起来很有趣(并且建议的语法在我看来更直观);还有建议的标准措辞,读起来很无聊,但更接近实际的内容。

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

输出:

< p > m [name1] [value1] = data1
m [name1] [value2] = data2
m [name2] [value1] = data1
m [name2] [value2] = data2
m [name3] [value1] = data1
m [name3] [value2] = data2 < / p >

为了填充地图,我使用了初始化器列表(这是一个c++ 11特性)。这有时可以方便地保持固定的初始化紧凑。

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

Code on Coliru

第一个解决方案是使用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
}
}