0

I have two vector that are related. The vector have the same size and their content match 1:1 in the sense that it could be refactores into a single vector of some struct. I am trying to remove the duplicates on the first vector, and this should match in the second vector.

v1 = [5 4 3 7 6 5 2 3]
v2 = [0 1 2 3 4 5 6 7]

Since 5 and 3 are repeated in v1, the result should be

v1 = [5 4 3 7 6 2]
v2 = [0 1 2 3 4 6]

The order can be change, as long as the relation is the same.

I am trying to achieve this using range-v3 library.

std::vector<unsigned int>  v1;
std::vector<double> v2;

auto v1Andv2 = range::views::zip(v1, v2);
ranges::sort(v1Andv2)
// ranges::unique(v1Andv2) // Doesnt compile
auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2), [](const auto &a, const auto &b) {
    // Since the second value of the zip is a double and could be slightly different, 
    // I am only interested in first one
    return std::get<0>(a) == std::get<0>(b);
});

v1.erase(???, std::end(v1));
v2.erase(???, std::end(v2));

I have no idea what I should put in the ??? to get the iterator each value in the zip. Also, why is ranges::actions::unique not working in this case?

cigien
  • 57,834
  • 11
  • 73
  • 112
jjcasmar
  • 1,387
  • 1
  • 18
  • 30

1 Answers1

0

The issue you are having is that you can't sort or otherwise modify a view. If you just create a vector of pairs, then you can use sort and unique without any trouble:

auto vv = ranges::views::zip(v1, v2) | 
          ranges::to<std::vector<std::pair<int,int>>>;

vv |= ranges::actions::sort([](auto const &a, auto const &b)
                            { return a.first < b.first; })
    | ranges::actions::unique([](auto const &a, auto const &b)
                              { return a.first == b.first; });

Here's a working demo.

You can even rearrange it back into the original order by sorting again, like this:

vv |= ranges::actions::sort([](auto const &a, auto const &b)
                            { return a.first < b.first; })
   | ranges::actions::unique([](auto const &a, auto const &b)
                             { return a.first == b.first; })
   | ranges::actions::sort([](auto const &a, auto const &b)
                           { return a.second < b.second; });

Here's a demo.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • using boost::zip_iterator this (sort + unique) should work out of the box without creating an intermediate container, no? – jjcasmar Apr 20 '20 at 23:27
  • I'm not sure how boost does this, but usually a view is not stored in memory, and you need elements to be in memory to do a sort. – cigien Apr 20 '20 at 23:29
  • This confuses me. If you think with iterators, you can have a pair of iterators, one for each container, and do the same operations you do with the first iterator to the second. I would have expcted a zip to work that way – jjcasmar Apr 20 '20 at 23:33
  • I'm not 100% sure, but I just tried out your version, and at least range-v3 doesn't work this way. – cigien Apr 20 '20 at 23:40
  • so you have test to sort a view range and it produces wrong results? thats weird, it produces good results here :-s – jjcasmar Apr 20 '20 at 23:42
  • Ok, but that should be a different question maybe? – cigien Apr 20 '20 at 23:45
  • yes, lets go back to original question. Given an iterator to a view object, how do I get its internal iterators? something like this (but actually working) auto it = std::begin(view(v1,v2)); ++it; it[0], it[1] – jjcasmar Apr 20 '20 at 23:46