How to zip vector of vector with range-v3
Asked Answered
D

4

6

(This is a follow-on to Sum vector with range-v3)

If I have two (or more) vectors, I can zip them together with range-v3 like this:

std::vector< int > v1{1,1,1};
std::vector< int > v2{2,2,2};

auto v = ranges::views::zip( v1, v2 )
  | ranges::views::transform( ... );

This works well, but in practice, I don't have explicit vectors, but I do have a vector of vectors. I'd like to do the following, but it doesn't give the same result. (In fact, I'm not sure what the result is, and I don't know how to determine what the result is!)


std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = ranges::views::zip( V )
  | ranges::views::transform( ... );

What can I do to zip a vector< vector > like I did to zip a few explicit vectors? I've tried using join along with stride, chunk, etc. but haven't found the magic combination.

Downfall answered 15/5, 2020 at 12:30 Comment(3)
btw, if you want to zip the fields of external vector it probably should not be vector but an array or tupleAshkhabad
The problem is I don't know how many sub-vectors there will be at compile time. I'm building up my vector (perhaps inefficiently) using push_back every time a new sub-vector is generated.Downfall
then there is no simple answer to your problem since zip has to know the external vector size in compileAshkhabad
A
1

I suppose if you don't know the size of external vector in compile time the only reasonable solution that remains is work around it. Instead of trying to zip it I would suggest going with accumulate on top of that as it seems more versatile in this situation.

std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = ranges::accumulate(
    V,
    std::vector<ResultType>(V[0].size()),
    [](const auto& acc, const auto& next) {
        auto range = ranges::views::zip(acc, next) | ranges::views::transform(...);
        return std::vector<int>(range.begin(), range.end());
    }
)

EDIT: I forgot the range has to be copied.

Ashkhabad answered 15/5, 2020 at 13:23 Comment(5)
Thanks for that suggestion. I like the idea, but I'm struggling to implement it. I'm not as familiar with accumulate so I'm not sure what the issue is.Downfall
@Downfall BTW also take a look at views::transpose, it might suit you even moreAshkhabad
Oh, good point. I hadn't thought of that. I love the range-v3 library, but there is just so much there that I simply don't even know what to ask. Did transpose make it into the C++20 standard?Downfall
@Downfall don't know it honestly, you have to just check latest draftAshkhabad
@Ashkhabad I don't think there is a views::transpose in range-v3. Could you point me to it?Tawny
P
3

ranges::views::zip( V ) zips only one vector, not its content. (similarly to std::make_tuple(v) which does std::tuple<vector<int>>).

Issue is that vector has runtime size, so construct a tuple like from its contents requires some help:

template <std::size_t ...Is, typename T>
auto zip_vector(std::index_sequence<Is...>, std::vector<std::vector<T>>& v)
{
    assert(N <= v.size());
    return ranges::views::zip(v[Is]...);
}

template <std::size_t N, typename T>
auto zip_vector(std::vector<std::vector<T>>& v)
{
    return zip_vector(std::make_index_sequence<N>(), v);
}

And then:

std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = zip_vector<2>( V )
  | ranges::views::transform( ... );
Pacification answered 15/5, 2020 at 12:50 Comment(0)
A
1

I suppose if you don't know the size of external vector in compile time the only reasonable solution that remains is work around it. Instead of trying to zip it I would suggest going with accumulate on top of that as it seems more versatile in this situation.

std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = ranges::accumulate(
    V,
    std::vector<ResultType>(V[0].size()),
    [](const auto& acc, const auto& next) {
        auto range = ranges::views::zip(acc, next) | ranges::views::transform(...);
        return std::vector<int>(range.begin(), range.end());
    }
)

EDIT: I forgot the range has to be copied.

Ashkhabad answered 15/5, 2020 at 13:23 Comment(5)
Thanks for that suggestion. I like the idea, but I'm struggling to implement it. I'm not as familiar with accumulate so I'm not sure what the issue is.Downfall
@Downfall BTW also take a look at views::transpose, it might suit you even moreAshkhabad
Oh, good point. I hadn't thought of that. I love the range-v3 library, but there is just so much there that I simply don't even know what to ask. Did transpose make it into the C++20 standard?Downfall
@Downfall don't know it honestly, you have to just check latest draftAshkhabad
@Ashkhabad I don't think there is a views::transpose in range-v3. Could you point me to it?Tawny
T
1

Based on the question you linked to, I think this particular question is an XY problem, i.e. there is no reason to involve zip to solve this problem, other than building upon the previous solution. While zip might have been a reasonable approach when the number of ranges was known at compile time, it just gets in the way when the number is only known at run-time.

So given that you have a vector<vector<int>>, where similar to the previous question, all the internal vectors are the same size, here's how I would write it in range-v3:

namespace rv = ranges::views;  

std::vector<std::vector<int>> v{{1,2,3},{4,5,6}};

int n = v.size();
int k = v[0].size();

auto vs = v | rv::join;

auto s = rv::iota(0, n + 1) 
       | rv::transform([=](int i){
           return ranges::accumulate(
                    vs | rv::drop(i) | rv::stride(k), 0);
         });

Here's a demo.

Tawny answered 15/5, 2020 at 16:39 Comment(0)
C
1

Zipping vector<vector<T>> is something you would want to do because it allows you to transpose these ranges.

The other answers sufficiently explain why you cant zip vector<vector<T>>.

However, you could write an explicit transpose adaptor as Eric Niebler did for his talk demonstrating range-v3

However, this is an expensive adaptor and expensive adaptors get ignored https://github.com/ericniebler/range-v3/issues/776

Circus answered 30/1, 2022 at 20:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.