From a vector of structs, get a vector that collects one of the fields for every struct

  • A+
Category:Languages

Let's say I have the following struct:

struct Point {     double X,Y,Z; }; 

and the following vector:

std::vector<Point> v; // populate v with random points 

Now, I want to call something like collect(v, X) and get a std::vector that contains the X values from the original struct vetor in it, for example:

v.push_back(Point{1.0, 2.0,  3.0}); v.push_back(Point{1.1, 0.0, -0.5}); auto ans = collect(v,X); // ans = [1.0, 1.1] 

I think this is a pretty common task, and I'm sure there's a good name for this that I couldn't come up with while asking (feel free to point me out!).

I can do this:

std::vector<double> collectX(std::vector<Point> v) {     std::vector<double> output;     for (auto elem : v) {         output.push_back(elem.X);     } } /* Repeat for each field the struct Point has... */ 

I know C++ has no reflection. I was wondering if there's a workaround for this? As you may imagine, the struct I am working with doesn't have just 3 fields, so writing a method for each fields is a bit daunting and inelegant.

 


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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: