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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.