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; });

Tags:

C++