从map <key,set <datatype =“”>&gt; </key,>访问元素

时间:2014-03-09 14:28:26

标签: c++ c++11 map set

我正在使用如下所示的数据结构:

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 ++参考。

6 个答案:

答案 0 :(得分:4)

我经常使用的模式(BOOST_FOREACH)是:

bool first = true;
for (auto const& e: collection) {
    if (first) { first = false; } else { out << ", "; }
    ...
}

使用ostream_iterator

,有一种STL方式可以做到这一点
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方法更具可读性。

答案 1 :(得分: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;
}

Output (live demo on coliru):

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

Another solution is to use ostream iterators

答案 2 :(得分:2)

您可以考虑这样的代码(使用标志以不同于以下字符串的方式打印集合中的第一个字符串,而不是以", "为前缀)。

请注意,由于您观察容器中的元素,因此您可能希望在range-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;
}

Live on Ideone

输出:

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

答案 3 :(得分: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';
}

答案 4 :(得分:1)

您将找不到“完美”的解决方案。基本上,你想为每个元素做同样的事情(凭借基于范围的for循环),但仍然以不同的方式处理一个案例。那不是一起来的。你可以做的一件事是使用一个额外的标志来不同地处理第一个元素:

bool first_element = true;
for ( auto i : data )
{
    if ( !first_element)
    {
        // print ","
    }
    // print i;    

    first_element = false;
}

一般情况下,如果这只是项目中某个地方的一小段代码,请不要过于担心。

答案 5 :(得分: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功能,因为我很懒,而且可能充满了拼写错误。如果有兴趣,我可以清理代码。