ChatGPT解决这个技术问题 Extra ChatGPT

如何循环遍历地图的 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";

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


R
Riot

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

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

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

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 因为按值访问会增加复制每个元素的效率;此外,如果您想修改内容,则需要通过引用(或指针)而不是值来访问元素。
P
Puppy

您可以使用迭代器。

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) 来避免潜在的复制。
A
Axel Gneiting
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 << element.second;
e
einpoklum

使用 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++。
K
Kevin Reid

做这样的事情:

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
u
user1438233

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

这个答案与 stackoverflow.com/a/27344958/3658660 有何不同?除了它到处复制的事实。
h
honk

正如 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][值2]=数据2

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

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

Code on Coliru


A
Amir Saniyan

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


您知道,有时将代码隐藏在右边距后面并不是一个好习惯。我知道它更安全,但完全模糊了代码的视野。去 auto 兄弟,否则使用 vim 的人会被 KO。
A
AmirSalar

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