Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ranges of ranges to vector of vectors

Tags:

c++

range-v3

Suppose I have a range of T's called rng. I can do

auto groups = ranges::view::group_by(rng, bin_op);

groups now being a range of ranges of T's.

I can also do this

auto groups = ranges::view::group_by(rng, bin_op) | ranges::to_vector;

to get a vector of ranges of T's. However this

auto groups = ranges::view::group_by(rng, bin_op)
            | ranges::to_vector
            | ranges::action::transform([] (auto r) { return r | ranges::to_vector; };

as well as

auto groups = ranges::view::group_by(rng, bin_op)
            | ranges::to_vector
            | ranges::action::transform([] (auto r) { return std::vector<T>{} | ranges::action::push_back; };

won't work since apparently ranges::action::transform() returns void in this case and "The result type of the function passed to action::transform must be writable back into the source range".

So how do I turn my ranges of ranges into a vector of vectors?

Note: Sorry for the bad tags, but I couldn't find a ranges/ranges-ts/ranges-v3 tag, I am not allowed to create one and couldn't use it in the title.

like image 414
Richard Vock Avatar asked Jul 20 '17 11:07

Richard Vock


2 Answers

You can use ranges::to to convert the range of ranges into a vector of vectors. For example:

#include <vector>
#include <iostream>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/group_by.hpp>
#include <range/v3/view/transform.hpp>

int main() {
    std::vector<int> rng {0,1,2,3,4,5,6,7,8,9};
    auto groups = ranges::view::group_by(rng, [](int i, int j){
        return j/3 == i/3;
    });

    auto vs = groups | ranges::to<std::vector<std::vector<int>>>;

    // Display the result: [[0,1,2],[3,4,5],[6,7,8],[9]]
    std::cout << ranges::view::transform(vs, ranges::view::all) << std::endl;
}

June 10, 2020: Previously, this answer recommended simply assigning from groups to a vector<vector<int>> variable because range-v3 used to support implicit conversions from views to containers. The implicit conversions were problematic, and so they were dropped. ranges::to is now the idiomatic way to do this.

like image 82
Eric Niebler Avatar answered Oct 05 '22 23:10

Eric Niebler


Assuming you are using Rangesv3, my reading of the docs gives me something like this:

auto groups = ranges::view::group_by(rng, bin_op)
        | ranges::view::transform( ranges::to_vector )
        | ranges::to_vector;

or maybe

auto groups = ranges::view::group_by(rng, bin_op)
        | ranges::view::transform( [] (auto r) { return r | ranges::to_vector; } )
        | ranges::to_vector;

(I recall that ranges::to_vector could be used in a function-style way, but I could be wrong, or things could have changed. The first one assumes it can be; the second doesn't.)

What this does is it first transforms the lazy range of ranges into a lazy range of vectors.

It then transforms the lazy range of vectors into a vector of vectors.

This works better (inside-out) because the intermediate products are lazy "on the outside". There may be a way to do it from the outside-in, but a vector of lazy ranges has to actually exist in a way that a lazy range of vectors does not.

like image 28
Yakk - Adam Nevraumont Avatar answered Oct 05 '22 23:10

Yakk - Adam Nevraumont