std::views::transform not working after std::views::chunk

139 Views Asked by At

I'm trying to get all entries in a std::multimap where two entries have the same key and then I want to multiply them up.

My idea so far was the following

// std::multimap<uint32_t, uint32_t> gears
auto gearRatios = gears
    | std::views::filter([&gears](const auto& pair) { return gears.count(pair.first) == 2; })
    | std::views::transform([&gears](const auto& pair) { return pair.second; })
    | std::views::chunk(2)
    | std::views::transform([](const auto& elements) { return std::accumulate(elements.begin(), elements.end(), 1, [](const auto& acc, const auto& value) { return acc * value; }); });
uint32_t sumOfGearRatios = std::accumulate(gearRatios.begin(), gearRatios.end(), 0);

But the compiler complains on the second transform with

[build] C:/Users/morit/Git/aoc2023/03/02_EngineSchematics.cpp:135:82: note:   deduced conflicting types for parameter '_InputIterator' ('std::counted_iterator<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::multimap<unsigned int, unsigned int> >, main()::<lambda(const auto:62&)> >, main()::<lambda(const auto:63&)> >::_Iterator<false> >' and 'std::ranges::take_view<std::ranges::subrange<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::multimap<unsigned int, unsigned int> >, main()::<lambda(const auto:62&)> >, main()::<lambda(const auto:63&)> >::_Iterator<false>, std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::multimap<unsigned int, unsigned int> >, main()::<lambda(const auto:62&)> >, main()::<lambda(const auto:63&)> >::_Iterator<false>, std::ranges::subrange_kind::unsized> >::_Sentinel<true>')
[build]   135 |         | std::views::transform([](const auto& elements) { return std::accumulate(elements.begin(), elements.end(), 1, [](const auto& acc, const auto& value) { return acc * value; }); });
[build]       |                                                                   ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build] C:/Users/morit/Git/aoc2023/03/02_EngineSchematics.cpp: In function 'int main()':
[build] C:/Users/morit/Git/aoc2023/03/02_EngineSchematics.cpp:135:9: error: no match for 'operator|' (operand types are 'std::ranges::chunk_view<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::multimap<unsigned int, unsigned int> >, main()::<lambda(const auto:62&)> >, main()::<lambda(const auto:63&)> > >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:64&)> >')
[build]   131 |     auto gearRatios = gears
[build]       |                       ~~~~~
[build]   132 |         | std::views::filter([&gears](const auto& pair) { return gears.count(pair.first) == 2; })
[build]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build]   133 |         | std::views::transform([&gears](const auto& pair) { return pair.second; })
[build]       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build]   134 |         | std::views::chunk(2)
[build]       |         ~~~~~~~~~~~~~~~~~~~~~~
[build]       |         |
[build]       |         std::ranges::chunk_view<std::ranges::transform_view<std::ranges::filter_view<std::ranges::ref_view<std::multimap<unsigned int, unsigned int> >, main()::<lambda(const auto:62&)> >, main()::<lambda(const auto:63&)> > >
[build]   135 |         | std::views::transform([](const auto& elements) { return std::accumulate(elements.begin(), elements.end(), 1, [](const auto& acc, const auto& value) { return acc * value; }); });
[build]       |         ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[build]       |                                |
[build]       |                                std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:64&)> >

I'm using

  • Windows 10

  • GCC 13.1.0 x64 installed through MSYS64

  • CMake 3.26.4

  • Ninja 1.11.1

Edit: After implementing all the input from the comments my code now looks like this:

// std::multimap<uint32_t, uint32_t> gears
    auto gearRatios = gears
        | std::views::chunk_by([](const auto& pairOne, const auto& pairTwo) { return pairOne.first == pairTwo.first; })
        | std::views::filter([](const auto& chunk) { return chunk.size() == 2; })
        | std::views::transform([](const auto& elements) { return std::ranges::fold_left(elements, 1, [](const auto& acc, const auto& value) { return acc * value; }); });
    uint32_t sumOfGearRatios = std::ranges::fold_left(gearRatios, 0, [](const auto& acc, const auto& value) { return acc + value; });

But it once again does not compile since in the filter lambda chunk has no attribute size(). I guess I could sum up all elements via a fold_left but is there a more elegant solution?

Example input

std::multimap<uint32_t, uint32_t> gears{
    {5, 20},
    {5, 30},
    {6, 50},
    {15, 10},
    {15, 5},
    {15, 2}
}

Expected output

20 * 30 = 600

1

There are 1 best solutions below

0
On BEST ANSWER

The subranges split by views::chunk_by are not sized_range in your example, so it does not provide the size() member. You need to use ranges::distance to calculate their sizes, which is linear time.

In addition, since the element of subranges is key-value pair, you also need to transform them into value ranges:

auto gearRatios = gears
    | std::views::chunk_by([](const auto& pairOne, const auto& pairTwo) { return pairOne.first == pairTwo.first; })
    | std::views::filter([](const auto& chunk) { return std::ranges::distance(chunk) == 2; })
    | std::views::transform(std::views::values) // <-- here
    | std::views::transform([](const auto& elements) { return std::ranges::fold_left(elements, 1, std::multiplies{}); });
auto sumOfGearRatios = std::ranges::fold_left(gearRatios, 0, std::plus{});

As in your first attempt, it might be concise to filter via count() :

auto gearRatios = gears
    | std::views::filter([&gears](const auto& pair) { return gears.count(pair.first) == 2; })
    | std::views::values
    | std::views::chunk(2)
    | std::views::transform([](const auto& elements) { return std::ranges::fold_left(elements, 1, std::multiplies{}); });
auto sumOfGearRatios = std::ranges::fold_left(gearRatios, 0, std::plus{});

Demo