Removing elements marked for removal with Ranges-V3

867 Views Asked by At

I've got two vectors:

struct MyData{
     double value; 
};
std::vector<int> remove_flags = {0, 1, 0, 0, 0, 0, 1, 0};
std::vector<MyData> data =      {{},{},{},{},{},{},{},{}}; 

The remove_flags vector contains an array of flags of the exact same size as data, each flag is either 0, or 1, where 1 means the data should be removed.

I would like to use remove_flags to remove elements from data in place, ie performing the erase remove idiom, but erasing based on values in remove_flags. The end result should be data with elements erased, and hopefully remove_flags with those same elements erased.

Doing this manually is annoying, and I wanted to use Range-v3 for this. I'm currently using C++17.

After looking through the documentation, I don't think I've found a solution, the closest thing I could come up with is:

auto result = ranges::views::zip(remove_flags, data) | ranges::actions::remove_if([](std::pair<const int&, const MyData&> pair){
    return pair.first != 0;
});

remove_flags.erase(result.first, remove_flags.end());
data.erase(result.second, data.end());

But actions cannot operate on the view zip, so this does not compile. If I switch ranges::actions::remove_if to ranges::views::remove_if a strange views object is returned, presumably one that has not actually performed the std::remove equivalent operation on the two vectors.

I could use contaner_to but that would involve a copy, and I don't want to pay that kind of unnecessary penalty for convenience. I've seen what I want accomplished in boost where actual zip pair iterators can be used to return two separate removal results.

Is this kind of pattern possible in Range-v3?

1

There are 1 best solutions below

0
On

Eager operation on existing ranges is the domain of algorithms. The only slightly tricky bit here is recovering iterators into the vectors since zip doesn't give you a direct way to get those. So instead recover them using the distance between the new end and the start of the range:

auto z = ranges::views::zip(remove_flags, data);
auto e = ranges::remove_if(z, [](auto&& r){ return r.first; });
data.erase(data.begin() + (e - z.begin()), data.end());
remove_flags.erase(remove_flags.begin() + (e - z.begin()), remove_flags.end());

Demo.