How to remove the Nth element in a range?

Viewed 199

I can write:

my_range | ranges::views::remove(3)

using the ranges-v3 library, to remove the element(s) equal to 3 from the range my_range. This can also be done in C++20 with

my_range | std::views::filter([](auto const& val){ return val != 3; })

But - how can I remove the element at position 3 in my_range, keeping the elements at positions 0, 1, 2, 4, 5 etc.?

2 Answers

Here's one way to do it:

#include <iostream>
#include <ranges>
#include <range/v3/view/take.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/concat.hpp>

int main() {
    const auto my_range = { 10, 20, 30, 40, 50, 60, 70 };
 
    auto index_to_drop = 3; // so drop the 40
    auto earlier = my_range | ranges::views::take(index_to_drop - 1);
    auto later = my_range | ranges::views::drop(index_to_drop);
    auto both = ranges::views::concat(earlier, later);
    for (auto const & num : both) { std::cout << num << ' '; }
}

This will produce:

10 20 30 50 60 70

... without the 40.

See it working on Godbolt. Compilation time is extremely poor though. Also, concat() is not part of C++20. Maybe in C++23?

The most straightforward way I can think of in range-v3 would be:

auto remove_at_index(size_t idx) {
    namespace rv = ranges::views;
    return rv::enumerate
         | rv::filter([=](auto&& pair){ return pair.first != idx; })
         | rv::values;
}

To be used like:

my_range | remove_at_index(3);

enumerate (and its more general cousin zip) is not in C++20, but will hopefully be in C++23.

Related