Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert std::vector<T> to a vector of pairs std::vector<std::pair<T,T>> using an STL algorithm?

I have a vector of integers:

std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};

Given that values.size() will always be even.

I simply want to convert the adjacent elements into a pair, like this:

std::vector<std::pair<int,int>> values = { {1,2}, {3,4} , {5,6}, {7,8} ,{9,10} };

I.e., the two adjacent elements are joined into a pair.

What STL algorithm can I use to easily achieve this? Is it possible to achieve this through some standard algorithms?

Of course, I can easily write an old school indexed for loop to achieve that. But I want to know what the simplest solution could look like using rangebased for loops or any other STL algorithm, like std::transform, etc.

like image 340
dev-here Avatar asked Sep 13 '25 15:09

dev-here


2 Answers

Once we have C++23's extension to <ranges>, you can get most of the way there with std::ranges::views::chunk, although that produces subranges, not pairs.

#include <iostream>
#include <ranges>
#include <vector>

int main()
{
    std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
    auto chunk_to_pair = [](auto chunk)
    {
        return std::pair(*chunk.begin(), *std::next(chunk.begin()));
    };
    for (auto [first, second] : values | std::ranges::views::chunk(2) | std::ranges::views::transform(chunk_to_pair))
    {
        std::cout << first << second << std::endl;
    }
}

Alternatively, you could achieve a similar result by ziping a pair of strided views

#include <iostream>
#include <ranges>
#include <vector>

int main()
{
    std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
    auto odds = values | std::ranges::views::drop(0) | std::ranges::views::stride(2);
    auto evens = values | std::ranges::views::drop(1) | std::ranges::views::stride(2);
    for (auto [first, second] : std::ranges::views::zip(odds, evens))
    {
        std::cout << first << second << std::endl;
    }
}

That last one can be generalised to n-tuples

template <size_t N>
struct tuple_chunk_t : std::ranges::range_adaptor_closure<tuple_chunk_t<N>>
{
    template <std::ranges::viewable_range R>
    auto operator()(R && r) const
    {
        auto impl = []<size_t... Is>(R && r, std::index_sequence<Is...>)
        {
            return std::views::zip(std::forward<R>(r) | std::views::drop(Is) | std::views::stride(N)...);
        };
        return impl(std::forward<R>(r), std::make_index_sequence<N>{});
    }
};

template <size_t N>
constexpr tuple_chunk_t<N> tuple_chunk;

Coliru link

like image 162
Caleth Avatar answered Sep 15 '25 05:09

Caleth


I'm not sure why you would require a standard algorithm when writing it yourself is roughly 5 lines of code (plus boilerplate):

template<class T>
std::vector<std::pair<T, T>> group_pairs(const std::vector<T>& values)
{
    assert(values.size() % 2 == 0);
    auto output = std::vector<std::pair<T, T>>();
    output.reserve(values.size()/2);
    for(size_t i = 0; i < values.size(); i+=2)
        output.emplace_back(values[i], values[i+1]);
    return output;
}

And call it like so:

std::vector<int> values = {1,2,3,4,5,6,7,8,9,10};
auto result = group_pairs(values)

Live Demo

like image 40
AndyG Avatar answered Sep 15 '25 04:09

AndyG