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
The
subrange
s split byviews::chunk_by
are notsized_range
in your example, so it does not provide thesize()
member. You need to useranges::distance
to calculate their sizes, which is linear time.In addition, since the element of
subrange
s is key-value pair, you also need to transform them into value ranges:As in your first attempt, it might be concise to filter via
count()
:Demo