ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.06k stars 437 forks source link

filtering by Proj enumerated sequence to get right indexes #1748

Closed UlrichVonRekkenin closed 1 year ago

UlrichVonRekkenin commented 1 year ago

Hi there,

For instance, a dummy type:

struct user_t {
    std::string name{};
    unsigned age{};
};

template<typename Stream>
Stream&
operator<<(Stream& s, const user_t& f) { return s << f.name << ' ' << f.age << ';'; }

template<typename Stream, typename I>
Stream&
operator<<(Stream& s, const std::pair<I, user_t>& p) { return s << p.first << " at " << p.second; }

As we can find in the examples it is easy to implement something simple:

    const std::vector<user_t> users = {{"boo", 12}, {"far", 20}, {"baz", 42}};

    auto filtered = users 
        | ranges::views::filter([](const auto& age){ return age%3==0; }, &user_t::age)
        | ranges::views::filter([](const auto& name){ return name[0]=='b'; }, &user_t::name);
    ranges::copy(filtered, ranges::ostream_iterator(std::cout, "\n"));

or

auto filtered = users 
        | ranges::views::enumerate 
        | ranges::views::filter([](const auto& p){ return p.second.name[0]=='b'; });
for (const auto [i, u] : filtered) std::cout << i << ')' << u;

But how to filter over a Proj by second_type (or other) of the enumerated element?

using proj_t =decltype((users | ranges::views::enumerate)[0]);
    auto filtered = users 
        | ranges::views::enumerate 
        | ranges::views::filter([](const auto& name){ return name[0]=='b'; }, &proj_t::second_type::name);
brevzin commented 1 year ago

The long way works:

auto filtered = users 
    | ranges::views::enumerate 
    | ranges::views::filter([](const auto& p){ return p.second.name[0]=='b'; })

Otherwise you can construct a projection by composing p.second with user.name.