1
#include <iostream> #include <vector> #include <iterator> using namespace std; struct Point { int x; int y; Point(int x, int y) : x(x), y(y) {} }; int main() { vector<Point> points; points.push_back(Point(1, 2)); points.push_back(Point(4, 6)); vector<int> xs; for(vector<Point>::iterator it = points.begin(); it != points.end(); ++it) { xs.push_back(it->x); } copy(xs.begin(), xs.end(), ostream_iterator<int>(cout, " ")); cout << endl; return 0; } 

I'm wondering how I would achieve the same result as the for loop above using an STL algorithm? I've tried a few things using for_each, but wasn't able to get it to work.

1 Answer 1

7

You wouldn't use std::for_each, but rather std::transform (you're transforming a point into a single number.)

For example:

#include <algorithm> // transform resides here #include <iostream> #include <iterator> #include <vector> struct Point { int x; int y; Point(int x, int y) : x(x), y(y) { } }; int point_to_int(const Point& p) { return p.x; } int main() { std::vector<Point> points; points.push_back(Point(1, 2)); points.push_back(Point(4, 6)); std::vector<int> xs; std::transform(points.begin(), points.end(), std::back_inserter(xs), point_to_int); std::copy(xs.begin(), xs.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; return 0; } 

Because you know the size of the container you'll be transforming, you might get a slight performance improvement from the following. I also find it more readable:

std::vector<int> xs; xs.reserve(points.size()); std::transform(points.begin(), points.end(), std::back_inserter(xs), point_to_int); 

And with boost::lambda along with boost::bind:

#include <algorithm> #include <iostream> #include <iterator> #include <vector> #include <boost/bind.hpp> #include <boost/lambda/lambda.hpp> struct Point { int x; int y; Point(int x, int y) : x(x), y(y) { } }; int main() { using namespace boost; std::vector<Point> points; points.push_back(Point(1, 2)); points.push_back(Point(4, 6)); std::vector<int> xs; xs.reserve(points.size()); std::transform(points.begin(), points.end(), std::back_inserter(xs), bind(&Point::x, lambda::_1)); std::copy(xs.begin(), xs.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << std::endl; return 0; } 

Removes the need to specify a function elsewhere. This keeps the code close to the calling site, and generally improves readability.

In C++0x, it will simply be:

std::transform(points.begin(), points.end(), std::back_inserter(xs), [](const Point& p){ return p.x; } ); 

(To the best of my knowledge, anyway)

Sign up to request clarification or add additional context in comments.

5 Comments

I hope C++0x brings some collection<a>.map(f: a=>b) => collection<b> facility along with its lambdas, because it's embarrassing what you have to do right now (Boost's transform_iterator will let you initialize the vector directly)
Regarding your optimization: Wouldn't simple xs.reserve(points.size()) suffice?
@GMan: Actually, what are you thinking now?! :) If you only reserve instead of resizing, you again need the back inserter!
Haha, I'm glad someone is here to keep my silliness in check. Man that's bad haha.
@GMan: I had a good laugh here about your good-humored comment. Anyway, now it's an elaborate, comprehensive answer I fully agree with. +1

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.