views:

168

answers:

4

I have a code that looks something like this:

struct First
{
    int f1;
    int f2;
};

struct Second
{
    First s1;
    int s2;
};

std::vector < Second > secondVec;

Second sec;
sec.s1 = First(); 

secondVec.push_back(sec);
secondVec.push_back(sec);

std::vector < First > firstVec;
firstVec.reserve(secondVec.size());

for (std::vector < Second >::iterator secIter = secondVec.begin(); 
         secIter != = secondVec.end();
         ++secIter)
{
    firstVec.push_back(secIter->s1);
}

I'd like to replace this ugly for loop with a simple stl function that could perhaps perform the equivalent process. I was thinking that maybe std::transform could help me here, but I'm unsure as to how this could be written.

I'd also be interested if boost has anything to offer here.

+3  A: 

Its not particularly difficult ... I tried this and it worked no problems.

struct First
{
    int f1;
    int f2;
};

struct Second
{
    First s1;
    int s2;
};

First Replace( Second& sec )
{
    return sec.s1;
}

and then used the following code to copy it

std::vector < Second > secondVec;

Second sec;
sec.s1.f1 = 0; 
sec.s1.f2 = 1; 
secondVec.push_back(sec);

sec.s1.f1 = 2; 
sec.s1.f2 = 3; 
secondVec.push_back(sec);

std::vector < First > firstVec;
firstVec.resize( secondVec.size() );
std::transform( secondVec.begin(), secondVec.end(), firstVec.begin(), Replace );
Goz
Why not to return `sec.s1` in `Replace` function?
Kirill V. Lyadvinsky
You could do that, yeah. I saw someone else's response and thought "Thats a good point". Will edit my post now.
Goz
Yeah, I was wondering why you were doing all this :)
Gab Royer
Although for some reason, I prefer using a back_inserter (while reserving before), your method of proceeding is as good if not a tad more efficient.
Gab Royer
It was one of those hammer out a little block of code to solve a problem without thinking it through entirely :)
Goz
+2  A: 

You were right with your intuition. Although since you are using an empty vector, you should use a back inserter for your output iterator.

It should look like something of the like :

std::transform(secondVec.being(), secondVec.end(), back_inserter(firstVec), yourFunctor)

And yourFunctor looking like this :

void youFunctor(First param)
{
  return param.s1;
}

Edit : Boost could help you with lambda function so you wouldn't have to create a separate functor for this task. You should also note that lambda function function are part of the TR1 and will be integrated to the C++ standard library.

Edit : Here is what Meredith was talking about with mem_fun (or member function adaptor).

 struct Second
{
    First s1;
    int s2;
    First getS1() const {return s1;};
};

And then the transform would look like this :

std::transform(secondVec.being(), 
               secondVec.end(), 
               std::back_inserter(firstVec), 
               std::mem_fun(&Second::getS1))
Gab Royer
You could also add a getter for `s1` to the `Second` class, and then use `std::mem_fun` to adapt the getter into a functor, rather than having to write an explicit functor. I prefer this because it keeps my code more organized, but some people seem to be scared of `<functional>`.
Meredith L. Patterson
Indeed, that would be far more elegant Meredith!
Gab Royer
Meredith, If you have time, I'd very much appreciate an example piece of code to illustrate this method - I'm intrigued!
Alan
This approach would be better if you were using a class though as it would allow you to encapsulate your data.
Gab Royer
+5  A: 

Define functor that will transform Second to First:

struct StoF { First operator()( const Second& s ) const { return s.s1; } };

Then use it in the following way:

transform( secondVec.begin(), secondVec.end(), back_inserter(firstVec), StoF() );

If your source vector contains a lot of elements you should consider resizing destination vector to make it work faster, as in @Goz answer:

firstVec.resize( secondVec.size() );
transform( secondVec.begin(), secondVec.end(), firstVec.begin(), StoF() );
Kirill V. Lyadvinsky
+7  A: 

If you have TR1 or Boost available, you could try this:

std::transform(secondVec.begin(),
               secondVec.end(),
               std::back_inserter(firstVec),
               std::tr1::bind(&Second::s1, _1));
Kristo
Great stuff, thank you.
Alan
Clean and elegant, +1
Gab Royer
Nice stuff: v.clever +1
jkp