Home > Mobile >  Flatten nested for loops with C 20 ranges
Flatten nested for loops with C 20 ranges

Time:10-06

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.

CodePudding user response:

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.

  • Related