从map<key, set<datatype>>中访问元素

5

我正在处理一个类似这样的数据结构:

map<string, set<string>> data;

到目前为止,我使用foreach循环来处理地图的工作没有问题,但现在我需要像这样打印地图中的数据:

KEY: elem1, elem2, elem3
KEY2: elem1, elem2, elem3

因为末尾缺少逗号,所以我不能再使用foreach循环了(对吗?)。由于我是C++、C++11和所有它可以提供的有趣内容的新手,我感到非常迷惑。

for ( auto i : data )
{
    cout << i.first << ": ";
    for ( size_t i = 0; i < /* size of the set */ - 1; i ++ )
        cout << j << ", ";

    cout << /* the last element of the set */ << endl;
}

我知道我想要什么,只是不懂语法,而且C++参考资料也没有帮助我多少。

谢谢你的回答,与此同时我会自己浏览C++参考资料。


无法处理单元素集合。 - n. m.
6个回答

4
我经常使用的模式(与BOOST_FOREACH一起),是:
bool first = true;
for (auto const& e: collection) {
    if (first) { first = false; } else { out << ", "; }
    ...
}

然而有一种STL的方法可以实现这一点,即使用 ostream_iterator

std::copy(collection.begin(), collection.end(),
          std::ostream_iterator<value_type>(out, ", "));

因此,您的示例变成如下:

for (auto const& pair: map) {
    out << pair.first << ": ";

    std::copy(pair.second.begin(), pair.second.end(),
              std::ostream_iterator<std::string>(out, ", "));
}

但是,老实说,我仍然觉得使用bool first = true的方法更易读。


对于有趣的ostream_iterator技巧,点个赞。 - Mr.C64

2
通常情况下(在多种语言中),我使用一个分隔符,将其初始化为空字符串,然后在循环中进行更改:
for (auto& i : data) {
    std::cout << i.first << ": ";

    std::string delim;     
    for (auto& s : i.second) {
        std::cout << delim << s;

        delim = ", ";
    }

    std::cout << std::endl;
}

输出(在coliru上的实时演示):

KEY: elem1, elem2, elem3
KEY1: elem1, elem2, elem3

另一个解决方案是使用 ostream 迭代器。

1
这太完美了,对我每周两次都有用。非常感谢 <3 - Lea
1
好的,但这会为每个循环迭代添加一个额外的字符串赋值。在大多数情况下,这可能不会对性能产生影响,但它仍然让我感到有些激进! :) - Christian Hackl
@ChristianHackl:我必须承认我也感到惊讶;虽然我猜在脚本语言中这没什么区别,在C++中(性能通常更受关注),它更加引人注目。 - Matthieu M.

2
您可以考虑像这样的代码(使用标记来以不同的方式打印集合中的第一个字符串,而后续字符串则使用前缀", ")。
请注意,由于您正在观察容器中的元素,因此可能希望在范围for循环中使用const auto&
#include <iostream>
#include <map>
#include <set>
#include <string>
using namespace std;

int main() {
    // Fill the data structure with some test data
    map<string, set<string>> data;
    data["Key1"].emplace("hello");
    data["Key1"].emplace("world");
    data["Key1"].emplace("test");
    data["Key2"].emplace("Ciao");
    data["Key2"].emplace("Mondo");

    // For each item in the outer map
    for (const auto& x : data) {
        // Print item's key
        cout << x.first << ": ";

        // Special output for the first string in the set
        bool first = true;

        // Note: x.first is the key, x.second is the associated set.

        // For each string in the set:
        for (const auto& s : x.second) {
            // Prefix every string except the first one with ", "
            if (!first) {
                cout << ", ";
            } else {
                first = false;
            }

            // Print current string in the set
            cout << s;
        }

        cout << endl;
    }

    return 0;
}

在 Ideone 上运行

输出:

Key1: hello, test, world
Key2: Ciao, Mondo

1

我通常使用迭代器来 连接 字符串。

for (auto& i : data) {
    cout << i.first << ':';
    auto p = i.second.begin();
    auto q = i.second.end();
    if (p != q) {
        cout << ' ' << *p;
        for (++p; p != q; ++p) {
            cout << ", " << *p;
        }
    }
    cout << '\n';
}

1
你不会找到一个“完美”的解决方案。基本上,你想为每个元素做同样的事情(通过基于范围的for循环),但仍然以不同的方式处理一个特殊情况。这是不兼容的。你可以做的一件事是使用额外的标志来处理第一个元素的不同情况:
bool first_element = true;
for ( auto i : data )
{
    if ( !first_element)
    {
        // print ","
    }
    // print i;    

    first_element = false;
}

一般情况下,如果这只是你项目中某个孤立的地方的一小段代码,就不用太担心它。


0
template<typename R>
struct not_last_t {
  R r;
  not_last_t(R&& in):r(std::forward<R>(in)){}
  not_last_t(not_last_t&&)=default;
  void operator=(not_last_t&&)=delete;
  // C++14 because lazy.  Can be written in C++11:
  auto begin() { using std::begin; return begin(r); }
  auto end() {
    using std::end; auto ret=end(r); 
    if(ret==begin()) return ret;
    return std::next(ret,-1);
  }
};
template<typename R>
not_last_t<R> not_last(R&&r){return {sts::forward<R>(r);}}

使用:

for(auto x:not_last(s))
  std::cout << x << ", ";
if (!s.empty())
  std::cout << s.back();

使用时高效且清晰明了。但在库中比较臃肿。

使用了部分C++14特性,因为我比较懒,并且可能存在一些拼写错误。如果您有兴趣,我可以对代码进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接