From a vector of structs, get a vector that collects one of the fields for every struct
so writing a method for each fields is a bit daunting and inelegant
An immediate fix for that is to pass the field identifier as an argument too.
std::vector<double> collect(double Point::* f, std::vector<Point> const& v) {
std::vector<double> output;
for (auto const& elem : v) {
output.push_back(elem.*f);
}
return output;
}
To be called like this:
collect(&Point::X, v);
If the types aren't always double
, then the above can easily be made a template over the member type:
template<typename T>
std::vector<T> collect(T Point::* f, std::vector<Point> const& v) {
std::vector<T> output;
for (auto const& elem : v) {
output.push_back(elem.*f);
}
return output;
}
And finally, the term you are looking for this sort of extraction is "projection". I.e, what one gets when projecting a function onto an axis, very roughly speaking. In our case, the function maps an index of the vector to a Point
, and the projection is onto the x
axis, as it were.
It can also be written on the fly with the C++ standard library, or with the ranges-v3 library. Projection is a very common operation with ranges of items, so many range-centric libraries will have the facilities to do it.
Use std::transform
, std::back_inserter
, and std::mem_fn
:
#include <functional>
//...
std::vector<Point> v{{0,1,2},{9,8,7}};
std::vector<double> x;
std::transform(v.begin(), v.end(), std::back_inserter(x),
std::mem_fn(&Point::x));
Compilers can typically optimize away the indirection behind std::mem_fn
.
You can use std::transform
and std::back_inserter
for that.
std::vector<Point> v;
v.push_back(Point{1.0, 2.0, 3.0});
v.push_back(Point{1.1, 0.0, -0.5});
std::vector<double> x;
std::transform(v.begin(), v.end(), std::back_inserter(x),
[](Point const& p) -> double { return p.x; });