Raildex
Raildex

Reputation: 4747

How to combine views::enumerate and views::filter?

I have a container that I want to filter.
I also need the index of the items later.
For that I want to use something like this:

auto items_to_remove = items | ranges::view::enumerate | ranges::view::filter(/*???*/);

What type do I have to use for the filter function?

Edit: Here is my code/ error message:

auto f = [](const auto& pair) {
            return true;
        };
auto items_to_remove = level.items | ranges::views::enumerate | ranges::views::filter(f);

enter image description here

Upvotes: 3

Views: 919

Answers (3)

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

Adding an enumerate::view makes the element that is passed to the filter a

ranges::common_pair<size_t, int&>&

So, just take the common_pair by const&.

auto f = [](const ranges::common_pair<size_t, int&>& pair) {
    // filter on pair.first to filter on the index
    // filter on pair.second to filter on the actual value in the vector
};

Demo

or simply:

auto f = [](auto&& pair) { ... }

Then

for (auto&&[idx, element] : v | views::enumerate | views::filter(f)) {
    element += 100;                            // elements are mutable here
    std::cout << idx << ' ' << element << '\n';
}

Demo

Upvotes: 5

Tom Huntington
Tom Huntington

Reputation: 3425

You should use auto lambdas with ranges, adding it static asserts if necessary.

If you don't want a copy happening you can simply have a predicate that takes its argument by value.

auto pred = [](auto pair) -> bool { return /*do something with T&*/ pair.second; };

The instantiated type of pair will be ranges::common_pair<size_t, T&> which you can copy around without copying the underlying T. You can call pair.second to get a T& in you predicate.

Shower around the following static assert just to be sure or delete the copy constructor for T because it is a very annoying when this copy turns out to be your bug.

std::vector<std::optional<T>> elements;
for (auto [index, opt] : elements
    | ranges::views::filter([](auto pair) -> bool { return pair.second; })
    )
{
    static_assert(std::is_reference_v<decltype(opt)>);
    opt->MethodOfT();
}

To understand why opt is a reference here see my question

Upvotes: 1

TrebledJ
TrebledJ

Reputation: 8997

Use std::pair<int, T>. .first is the index. .second is the element.

int main() {
    auto v = std::vector{0, 1, 23, 3, 42, 50, 81, 4, -1};
    auto f = [](std::pair<int, int> p) { return p.first % 2 == 0 && p.second >= 10; };
    for (auto [i, e] : v | views::enumerate | views::filter(f)) {
        std::cout << i << " -> " << e << "\n";
    }
}

(Godbolt Demo)

Upvotes: 0

Related Questions