【问题标题】:Accessing elements from map<key, set<datatype>>从 map<key, set<datatype>> 访问元素
【发布时间】:2014-03-09 14:28:25
【问题描述】:

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

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

【问题讨论】:

  • 不适用于 1 元素集。

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


【解决方案1】:

我经常使用的一个模式(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 方法更具可读性。

【讨论】:

  • +1 为有趣的ostream_iterator 技术。
【解决方案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

【讨论】:

  • 这对我来说非常完美,每周两次有用。非常感谢
  • 好吧,但这会为每个循环迭代添加一个额外的字符串分配。在大多数情况下,它可能不会对性能产生影响,但它仍然让我觉得这是一种相当激进的方法! :)
  • @ChristianHackl:我必须承认我也对此感到惊讶;虽然我猜在脚本语言中它几乎没有什么区别,但在 C++ 中(通常更关注性能),它更加华丽。
【解决方案3】:

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

请注意,由于您正在观察容器中的元素,因此您可能希望在 range-for 循环中使用 const auto&amp;

#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

【讨论】:

    【解决方案4】:

    我通常使用迭代器来加入字符串。

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

    【讨论】:

      【解决方案5】:

      您不会找到“完美”的解决方案。基本上,您希望对每个元素执行相同的操作(借助基于范围的 for 循环),但仍以不同的方式处理一个案例。这不在一起。你可以做的一件事是使用一个额外的标志来不同地处理第一个元素:

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

      一般来说,如果这只是项目中某个孤立位置的一小段代码,请不要太担心。

      【讨论】:

        【解决方案6】:
        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 特性,因为我很懒,而且可能有很多错别字。如果有兴趣我可以清理代码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-19
          • 1970-01-01
          • 2022-01-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多