Home > Net >  How to avoid an explicit loop when pulling out a vector of a particular member from a vector of a cl
How to avoid an explicit loop when pulling out a vector of a particular member from a vector of a cl

Time:09-17

Suppose I have a class

struct Foo {double x; double y;}

and std::vector<Foo> xy;

I also have a function to pull out the x members:

std::vector<double> bar(const std::vector<Foo>& xy);

which loops through each element of xy.

Is there a way of using the C standard library so I can avoid an explicit for loop?

CodePudding user response:

You can use std::transform. E.g.

std::vector<double> bar(const std::vector<Foo>& xy) {
    std::vector<double> rt;
    rt.reserve(xy.size());
    std::transform(std::begin(xy), 
                   std::end(xy), 
                   std::back_inserter(rt), 
                   [](const Foo& f) { return f.x; });
    return rt;
}

For comparison here's the version using range-based for loop. As @463035818_is_not_a_number commented it needs less typing.

std::vector<double> foo(const std::vector<Foo>& xy) {
    std::vector<double> rt;
    rt.reserve(xy.size());
    for (auto const & f : xy) rt.push_back(f.x);
    return rt;
}

CodePudding user response:

Using C 20's ranges offers a somewhat briefer alternative to std::transform:

std::vector<double> filtered;
filtered.reserve(xy.size());
std::ranges::copy(
  std::ranges::views::transform(xy, &Foo::x),
  std::back_inserter(filtered));

Or, as pointed out by @StoryTeller, as an alternative and even briefer approach to the init-empty-and-back-insert, use a view to directly intitialize the filtered vector (an approach that would allow it to be const):

auto view = xy | std::ranges::views::transform(&Foo::x);
std::vector<double> const filtered(view.begin(), view.end());

CodePudding user response:

Well, behind the scenes there obviously has to be a loop somewhere. So it seems to me that the STL included for_each for this exact type of problem.

We can write the Bar function like so:

#include <algorithm>

auto Bar (const std::vector<Foo> & xy)
{
    std::vector<double> bar;
    auto extract_x = [&](const Foo & f){bar.push_back(f.x);};

    std::for_each(xy.begin(), xy.end(), extract_x);
    
    return bar;
}

Demo

By removing the auto we can also do this with c 11: Demo

However, I agree with others here that this doesn't seem more useful than a plain old loop. Maybe it's more useful in some real-world scenario - but I can't think of one.

  • Related