I have a single even-sized vector that I want to transform into a vector of pairs where each pair contains always two elements. I know that I can do this using simple loops but I was wondering if there is a nice standard-library tool for this? It can be assumed that the original vector always contains an even amount of elements.
Example:
vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
vector<pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };
CodePudding user response:
Use Range-v3:
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/chunk.hpp>
using namespace ranges;
using namespace ranges::views;
int main() {
std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<std::pair<int, int>> goal { {1, 2}, {3, 4}, {5, 6}, {7, 8} };
auto constexpr makePairFromRangeOf2 = [](auto two){
return std::make_pair(two.front(), two.back());
};
auto result = origin | chunk(2)
| transform(makePairFromRangeOf2)
| to_vector;
}
Notice that if you only have to loop on result
, you can leave | to_vector
out, because you'll still be able to do result.begin()
and result.end()
, for instance.
If you don't need the inner containers to truly be std::pair
s, but your just happy with calling, say, result.front().front()
instead of result.front().first
, then you can leave also the transform
, and just be happy with auto result = origin | chunk(2);
.
You don't mention why you only want a standard solution. However consider that <ranges>
is standard in C 20. Unfortunately that feature is not as powerful as pre-C 20 Range-v3 library. But it will be at some point (C 23?), I think without any doubts.
CodePudding user response:
As mentioned by @康桓瑋 , if you're willing to also use the ranges-v3 library, you can use a chunk()
view:
std::vector origin = {1, 2, 3, 4, 5, 6, 7, 8};
auto goal = v | ranges::views::chunk(2) | ranges::to<std::vector>;
See it working on GodBolt.
Unlike my other answer, this will be perfectly valid language-wise.
Caveats:
- This will make a copy of your data!
- Likely to introduce a bunch of stuff (error strings, exception handlers etc.) into your object code.
- The ranges library increases compilation times significantly (although perhaps less so with C 20 enabled?)
- Not based on the standard library - but apparently
chunk()
andto()
will be in C 23, so upto a slight syntax tweak (e.g. addingstd::
), this will be valid C 23 with only standard library includes. - The elements of
goal
are notstd::pair
s, bur rather ranges. You will need to get the first and second, or first and last, elements to make an actual pair.
CodePudding user response:
I have a function for handling both even and odd number of elements in a vector. What it does that it takes another parameter to add a number at the end of pair. I don't think there's any standard tool/library to do so.
Here is the try it online link.
#include <iostream>
#include <vector>
std::vector<std::pair<int, int>> vec_to_pair(const std::vector<int> &my_vec, int odd_origin)
{
std::vector<std::pair<int, int>> val;
for (std::size_t i = 0; i < my_vec.size(); i )
{
int sec_val;
if (i < my_vec.size() - 1)
sec_val = my_vec[i 1];
else if (my_vec.size() % 2 != 0)
sec_val = odd_origin;
else
break;
int data[] = {my_vec[i ], sec_val};
val.push_back({data[0], data[1]});
}
return val;
}
void print(const std::vector<std::pair<int, int>> &vec)
{
std::cout << "{ ";
for (auto &&i : vec)
std::cout << "{ " << i.first << ", " << i.second << " } ";
std::cout << " }" << std::endl;
}
int main(void)
{
std::vector<int> vec1 = {1, 2, 3, 4, 5}; // odd
std::vector<int> vec2 = {1, 2, 3, 4, 5, 6}; // even
auto x1 = vec_to_pair(vec1, -1);
auto x2 = vec_to_pair(vec2, 0);
print(x1);
print(x2);
return 0;
}
CodePudding user response:
The intuitive, but unfortunately invalid, way to do it
There's a quick-and-dirty approach, which will kinda-hopefully-maybe do what you asked for, and will not even copy the data at all... but the downside is that you can't be certain it will work. It relies on undefined behavior, and can thus not be recommended. I'm describing it because I believe it's what one imagines, intuitively, that we might be able to do.
So, it's about using std::span
with re-interpretation of the vector data:
std::vector<int> origin {1, 2, 3, 4, 5, 6, 7, 8};
auto raw_data = reinterpret_cast<std::pair<int, int>*>(origin.data());
std::span<std::pair<int, int>> goal { raw_data, origin.size()/2 };
See this on GodBolt
Caveats:
reinterpret_cast
is "dirty". It officially results in undefined behavior, and what it does in practice depends on the compiler and platform. You can circumvent this if you also forget aboutstd::pair
's, and instead use a 2-dimensional mdspan instead. Of course,mdspan
's are not in the standard library.- This is C 20. Before C 20 you can still use span's, but they're not in the standard library.
- Specifically, if you're on a platform where unaligned accesses are not allowed, in may be a problem to insist on there being twice-the-size-of-int values at
goal.data()
.