3

Is there any standard functionality to create a range/view over all pairs? The following code illustrates the view I am looking to create:

std::unordered_map<std::string, std::vector<int>> m{{"Foo", {1,2}}, {"Hello", {4,5}}};
auto view = ???;
std::vector<std::pair<std::string, int>> v{view.begin(), view.end()};
std::vector<std::pair<std::string, int>> out1{{"Foo", 1}, {"Foo", 2}, {"Hello", 4}, {"Hello", 5}};
std::vector<std::pair<std::string, int>> out2{{"Hello", 4}, {"Hello", 5}, {"Foo", 1}, {"Foo", 2}};
assert(v == out1 || v == out2);

Note: It is trivial to write a nested for loop to iterate over this structure.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
MarkB
  • 672
  • 2
  • 9

2 Answers2

5

If C++23 eventually adopts views::cartesian_product, here is another way

std::unordered_map<std::string, std::vector<int>> m{
   {"Foo", {1,2}}, {"Hello", {4,5}}};
auto view = m | views::transform([](auto& p) {
         return views::cartesian_product(views::single(p.first), p.second); })
              | views::join;

Demo

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
4

Yes, combine std::ranges::views::join with std::ranges::views::transform and some lambdas.

using namespace std::ranges::views;
auto to_pairs = [](auto & pair){ 
    auto to_pair = [&key=pair.first](auto & value){ return std::pair{ key, value }; };
    return pair.second | transform(to_pair); 
};
auto view = m | transform(to_pairs) | join | common;
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • I got a compiler error when trying to construct the vector from the view directly. However, std::ranges::copy(view, std::back_inserter(v)) works great! – MarkB May 03 '22 at 14:39
  • @MarkB oops, need to ensure the view is a `std::ranges::common_range`, see edit – Caleth May 03 '22 at 14:49
  • Returning `std::pair{key, value}` is more efficient – MarkB Dec 28 '22 at 16:16