0

Sometimes I need to "double for loop" and since I do nothing in outer for loop for first loop variable beside passing it to inner for loop I wonder if it can be done elegantly with C++20 ranges.

Example of nested for loops I would like to "flatten".

struct Person{
    std::string name = "Bjarne";
};

std::vector persons{Person{}, Person{}};

int main() {
    for (const auto& person: persons) {
        for (const auto& ch: person.name) {
            std::cout << ch << std::endl;
        }
    }
}

Best what I can think of is:

std::ranges::for_each(persons | std::views::transform(&Person::name) | std::views::join, [](const char& ch){
    std::cout << ch << std::endl;
});

but I wonder if there is a simpler way.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    I'm not sure it's simpler, but it's a little shorter: `for(char ch : persons | std::views::transform(&Person::name) | std::views::join) std::cout << ch << '\n';` – Ted Lyngmo Oct 05 '21 at 12:56
  • 1
    BTW, `std::cout` can directly output `std::string` :) – Jarod42 Oct 05 '21 at 12:58
  • @Jarod42 I presume you are just joking(not sure since :) not ;) ) but it is just an example, in reality it could be a vector < list < SomeClass > > – NoSenseEtAl Oct 05 '21 at 13:06

1 Answers1

3

Yeah, what you propose is correct. Except it can still be a range-based for statement, you don't have to switch to an algorithm:

for (const auto& ch : persons
                    | std::views::transform(&Person::name)
                    | std::views::join)
{
    // ... 
}

Most languages use the name map instead of transform, and flatten instead of join (indeed your question title asks about flattening). And then it's common to put these two together with a new algorithm called flat_map which does both of these things together.

We can do that too:

inline constexpr auto flat_map = [](auto f){
    return std::views::transform(f) | std::views::join;
};

for (const auto& ch : persons | flat_map(&Person::name))
{
    // ... 
}

Although in C++ I suppose this would be called transform_join? Meh. flat_map all the way.


Pro-tip: use fmt::print to print ranges, less to write and it already comes out formatted.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • @ fmt : I know, I was just using chars as a simple example, in retrospect it was a mistake, since other people also thought I dont know to print strings :). I did not want to have a vector of strings as a member inside struct since it would be more complicated... mistakes were made :) – NoSenseEtAl Oct 05 '21 at 15:21