Search code examples
c++range-v3

Apply projection prior to piping to view or action


I would like to apply a projection prior to piping a range into an action or view. Consider the following example:

#include <iostream>
#include <range/v3/view/map.hpp>
#include <range/v3/action/sort.hpp>
#include <range/v3/algorithm/for_each.hpp>

int main()
{
    std::vector<std::string> strings{"1.2","100.2","11.2","0.2","2.2"};
    ranges::for_each(strings, [](const auto& str) { std::cout << str << std::endl; });
    
    strings | ranges::views::all | ranges::actions::sort;
    
    std::cout << std::endl;
    ranges::for_each(strings, [](const auto& str) { std::cout << str << std::endl; });

    return 0;
}

Say that I wish to convert the string to a double prior to sorting. Using the functional approach, this is possible:

strings |= ranges::actions::sort(std::less{}, [](const auto& str) { return std::stod(str); });

However I'm wondering if there is a pipeline format which allows this instead, which applies this projection for all following operations, for example:

strings | ranges::views::projection([](const auto&str ) { return std::stod(str); }) | ranges::actions::sort;

Note that using ranges::views::transform doesn't work as it creates a new range instead of a projection.

I'm also not sure if there's a caveat for using strings | ranges::views::all | ranges::actions::sort instead of strings |= ranges::actions::sort which is recommended in the range-v3 documentation.


Solution

  • Update(2021.01.26):

    Doing actions on projected views might cause confusion. E.g. imagine a projection function with signature int&(std::pair<int, int>&) / int&&(std::pair<int, int>&&) that return the first component of a pair, apply that projection on a range of std::pair<int, int>. What should happen if we filter?

    • It should return a range of int because we passed it a range of int.
    • It should also return a range of std::pair<int, int> because we really want to filter the underlying range of std::pair<int, int>.

    But it does not know we want which.

    So it seems that it is not appropriate to treat projection as a standalone operation on range this time, instead it is more clear if the projection operation is an optional parameter of filter or sort.


    Old answer(2021.01.15):

    It is inefficient to implement sorting with projection and perhaps that's why it's not provided. You have to store the result of projection explicitly:
    c++ - Why can't a range be sorted in range-v3? - Stack Overflow
    Why can't a range be sorted in range-v3?

    The standard version says the complexity of projection is 𝓞(N·log(N)) (so it may be faster to project the elements and store them before sorting):
    std::ranges::sort - cppreference.com
    https://en.cppreference.com/w/cpp/algorithm/ranges/sort