Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to do this (small task - std::transform?)?

51 views
Skip to first unread message

JiiPee

unread,
May 19, 2017, 11:15:22 AM5/19/17
to
say I have a 2 structs

struct Person

{

int age;

string name;

};


struct Person2

{

int age;

int height;

string name;

string mobilenumber;

};

and vectors:

vector<Person> persons;

vector<Person2> persons2;

// add 10 items into persons ....

// add 10 items into persons2 ....


Now, which std-function I can use to copy *only* the age-values from
persons vector into persons2?

I was thinking using std::transform, but could not yet find how. I need
to use some other std function?

I know how to do it with a for-loop of course, but would like to know
how to do it with std-functions.

thanks

JiiPee

unread,
May 19, 2017, 11:47:25 AM5/19/17
to
On 19/05/2017 16:36, Stefan Ram wrote:
> JiiPee <n...@notvalid.com> writes:
>> I know how to do it with a for-loop of course, but would like to know
>> how to do it with std-functions.
> ::std::for_each, SCNR.
>
>
but how? for_each works only for ONE object, right? You cannot copy from
one vector to another using that I think.

JiiPee

unread,
May 19, 2017, 3:10:06 PM5/19/17
to
Thanks, nice. Just what I wanted. I ll study it now.
> It's contrived, but possible.
>
> #include <algorithm>
> #include <chrono>
> #include <initializer_list>
> #include <iostream>
> #include <iterator>
> #include <ostream>
> #include <random>
> #include <string>
> #include <vector>
>
> using namespace ::std::literals;
>
> int main()
> { struct person0 { int age{ 0 }; ::std::string name; };
> struct person1
> { int age{ 1 }; int height{ 0 };
> ::std::string name; ::std::string mobile; };
> ::std::vector< person0 >vector0( 10 );
> ::std::vector< person1 >vector1( 10 );
>
> ::std::for_each
> ( ::std::cbegin( vector0 ),
> ::std::cend( vector0 ),
> [ &vector0, &vector1 ]( person0 const & p0 )
> { auto offset = &p0 - &vector0.at( 0 );
> auto & target = vector0.at( offset );
> target.age = p0.age; } );
>
> ::std::for_each
> ( ::std::cbegin( vector1 ),
> ::std::cend( vector1 ),
> []( person1 const & p1 )
> { ::std::cout << p1.age; } );
>
> ::std::cout << '\n'; }
>

Alf P. Steinbach

unread,
May 19, 2017, 8:35:10 PM5/19/17
to
On 19-May-17 5:14 PM, JiiPee wrote:
> say I have a 2 structs
>
> struct Person
>
> {
>
> int age;
>
> string name;
>
> };
>
>
> struct Person2
>
> {
>
> int age;
>
> int height;
>
> string name;
>
> string mobilenumber;
>
> };
>
> and vectors:
>
> vector<Person> persons;
>
> vector<Person2> persons2;
>
> // add 10 items into persons ....
>
> // add 10 items into persons2 ....
>
>
> Now, which std-function I can use to copy *only* the age-values from
> persons vector into persons2?

for( int i = 0, n = persons.size(); i < n; ++i )
{
persons2[i].age = persons[i].age;
}


> I was thinking using std::transform, but could not yet find how. I need
> to use some other std function?

No. I recommend just a simple, clear loop as above. Perhaps with an
`assert` that the vectors are of equal size.


> I know how to do it with a for-loop of course, but would like to know
> how to do it with std-functions.

Oh. There's no suitable such function. It would be contrived.


Cheers & hth.,

- Alf


JiiPee

unread,
May 19, 2017, 11:04:04 PM5/19/17
to
On 20/05/2017 01:34, Alf P. Steinbach wrote:
> for( int i = 0, n = persons.size(); i < n; ++i )
> {
> persons2[i].age = persons[i].age;
> }


sure, ok so not good to even consider functions. But it was good to know
by Stefan how to do it anyway.

you dont prefer:

for( size_type i{0}, size = persons.size(); i < size; ++i )

?

Alf P. Steinbach

unread,
May 19, 2017, 11:46:36 PM5/19/17
to
On 20-May-17 5:03 AM, JiiPee wrote:
>
> you dont prefer:
>
> for( size_type i{0}, size = persons.size(); i < size; ++i )
>
> ?

Using a modulo-2^n arithmetic type for a general integer, as opposed to
for bit-level operations, is ungood in itself. Mainly because the
promotion rules in modern C++ then are likely to cause unintended and
un-noticed wrap-around promotions that cause havoc. My usual example is
that the expression `string( "Bah!" ).length() < -5` is true.

Doing the unfortunately not uncommon dance with trying to use the
minimal unsigned type is an additional abomination on top of the first,
adding needless extra work up front, in addition to the extra work later
on for sorting out the bugs from the modulo-2^n behavior.

As I see it, just use appropriate simple types and appropriate simple
constructs, and don't try to be fancy.

BUT: if the vector can have more than 2G entries, then make sure to use
an index type that can deal with that, such as `ptrdiff_t`. If you use
that by default then you're safe at almost no cost. So it can be a good
idea to define self-describing names for it, e.g. `Size` and `Index`.

Summing up: a modulo-2^n type does not yield shorter code, has no
advantage, and due to modern C++ promotion rules that practice can and
will bite you in the rear end at the least convenient moment.


Cheers!,

- Alf

JiiPee

unread,
May 20, 2017, 1:32:47 PM5/20/17
to
On 20/05/2017 04:46, Alf P. Steinbach wrote:
> On 20-May-17 5:03 AM, JiiPee wrote:
>>
>> you dont prefer:
>>
>> for( size_type i{0}, size = persons.size(); i < size; ++i )
>>
>> ?
>
> Summing up: a modulo-2^n type does not yield shorter code, has no
> advantage, and due to modern C++ promotion rules that practice can and
> will bite you in the rear end at the least convenient moment.
>

Sure, also Bjarne said similar things (he does not like uint).
But how about that
int i{0};
?
the way to initialize int? rather than
int i = 0;

Bonita Montero

unread,
May 20, 2017, 1:57:20 PM5/20/17
to
With std::transform ...


#include <string>
#include <vector>
#include <algorithm>

using namespace std;

struct Person
{
int age;
string name;
};

struct Person2
{
int age;
int height;
string name;
string mobilenumber;
};

void f( vector<Person> &vp, vector<Person2> &vp2 )
{
vp2.reserve( vp.size() );
transform( vp.begin(), vp.end(), back_inserter( vp2 ),
[]( Person &p ) { Person2 p2; p2.age = p.age; return p2; } );
}

Cholo Lennon

unread,
May 20, 2017, 3:01:39 PM5/20/17
to
+1, the best solution IMO (and kudos for showing the usage pattern:
reserve for vectors, transform/back_inserter)


--
Cholo Lennon
Bs.As.
ARG

Bonita Montero

unread,
May 20, 2017, 3:02:53 PM5/20/17
to
> ::std::cout << '\n'; }

Not '\n' but endl. endl is flushing the stream.
And your formatting-style is disgusting.

JiiPee

unread,
May 20, 2017, 3:09:09 PM5/20/17
to
On 20/05/2017 19:30, Stefan Ram wrote:
> JiiPee <n...@notvalid.com> writes:
>> int i{0};
>> int i = 0;
> In the first case, narrowing is forbidden, and it is the

but we dont need narrowing here. int to int.. zero is int.

> "/uniform/ initialization syntax". Another possibility is
>
> auto i{ 0 };
>
> (since C++17). However, a zero initialization can also be
> written more concise as
>
> int i {};

for me

int i{0};
is clearer because we see clearer that i starts from 0 in that for loop.


>
> .
>

Alf P. Steinbach

unread,
May 20, 2017, 9:01:53 PM5/20/17
to
Hm!

As I understood the task:

> vector<Person> persons;
> vector<Person2> persons2;
>
> // add 10 items into persons ....
> // add 10 items into persons2 ....
>
> Now, which std-function I can use to copy *only* the age-values from
> persons vector into persons2?

… it involves

• Two vectors of equal size, one hold Person items and the other holding
Person2 items. I.e. the latter is not empty.

• The `age` values in the latter should be /changed/.

It looks to me as if you're assuming instead that the `vp2` vector is
initially empty. Also, an assumption that at the end it should hold only
age values, with other items default-initialized. But this means that
the `height` items will have indeterminate values.

For example, when I added the following little driver code,

#include <iostream>
auto main()
-> int
{
vector<Person> vp = { {1, "One"}, {2, "Two"}, {3, "Three" } };
vector<Person2> vp2;
f( vp, vp2 );
for( auto const& p : vp2 )
{
cout << p.age << " " << p.height << " `" << p.name << "`"
<< endl;
}
}

which hopefully conforms to the assumptions in your code, it produced

1 4208845 ``
2 4208845 ``
3 4208845 ``

Changing the default initialization to one that carries over the data is
trivial, like this,

#include <assert.h>
void f_fixed( vector<Person> &vp, vector<Person2> &vp2 )
{
assert( vp2.size() == 0 );
vp2.reserve( vp.size() );
transform( vp.begin(), vp.end(), back_inserter( vp2 ),
[]( Person &p ) { Person2 p2{ p.age, 0, p.name }; return
p2; } );
}

which gives the output

1 0 `One`
2 0 `Two`
3 0 `Three`

Then it's very clear that this is really a copying of transformed
values. I think that's very likely what the OP was really after. I.e.,
that the request to modify values was an X/Y-question.

• • •

As a matter of style I would add `const` for the vector of original
items, and instead of using an out-argument for the result I would
simply return it, like this:

auto f2( vector<Person> const& vp )
-> vector<Person2>
{
vector<Person2> result;
result.reserve( vp.size() );
transform( vp.begin(), vp.end(), back_inserter( result ),
[]( Person const& p ) { return Person2{ p.age, 0, p.name };
} );
return result;
}

E.g. this allows the driver program to use `const`, and there's no need
for the `assert` any more – one degree of freedom for bugs is removed.


Cheers!,

- Alf

JiiPee

unread,
May 20, 2017, 11:56:00 PM5/20/17
to
On 21/05/2017 02:01, Alf P. Steinbach wrote:
>
> Hm!
>
> As I understood the task:
>
>> vector<Person> persons;
>> vector<Person2> persons2;
>>
>> // add 10 items into persons ....
>> // add 10 items into persons2 ....

And then possibly set all the member variables into some value.

>>
>> Now, which std-function I can use to copy *only* the age-values from
>> persons vector into persons2?
>
> … it involves
>
> • Two vectors of equal size, one hold Person items and the other
> holding Person2 items. I.e. the latter is not empty.
>
> • The `age` values in the latter should be /changed/.

all the age values from the first should be copied to second (in the
same order). And no other values touched/modified -they should remain
the same as they were.
so it copied ALL the values from the first? this is not what I wanted.

Alf P. Steinbach

unread,
May 21, 2017, 1:03:12 AM5/21/17
to
On 21-May-17 5:55 AM, JiiPee wrote:
>> Changing the default initialization to one that carries over the data
>> is trivial, like this,
>>
>> #include <assert.h>
>> void f_fixed( vector<Person> &vp, vector<Person2> &vp2 )
>> {
>> assert( vp2.size() == 0 );
>> vp2.reserve( vp.size() );
>> transform( vp.begin(), vp.end(), back_inserter( vp2 ),
>> []( Person &p ) { Person2 p2{ p.age, 0, p.name }; return
>> p2; } );
>> }
>>
>> which gives the output
>>
>> 1 0 `One`
>> 2 0 `Two`
>> 3 0 `Three`
>
> so it copied ALL the values from the first? this is not what I wanted.

Okay, then you CAN do this, in order to just modify the `age` values,
leaving the other fields' values unchanged:

#include <assert.h>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

struct Person
{
int age;
string name;
};

struct Person2
{
int age;
int height;
string name;
string mobilenumber;
};

void f( vector<Person> const& v, vector<Person2> &v2 )
{
assert( v.size() == v2.size() );
transform(
v.begin(), v.end(), // Source 1, for arg `p` below.
v2.begin(), // Source 2, for arg `p2` below.
v2.begin(), // Destination
[]( Person const& p, Person2 p2 ) { p2.age = p.age; return
p2; }
);
}

#include <iostream>
auto main()
-> int
{
vector<Person> const vp = { {1, "One"}, {2, "Two"}, {3, "Three"
} };
vector<Person2> vp2 = { {0, 10, "En"}, {0, 20, "To"}, {0, 30,
"Tre"} };
f( vp, vp2 );

for( auto const& p : vp2 )
{
cout << p.age << " " << p.height << " `" << p.name << "`"
<< endl;
}
}

But as I wrote earlier, it's contrived. And as I forgot to mention,
sorry, it's also needlessly inefficient and brittle. Better just use a
straightforward `for` loop. ;-)

Bonita Montero

unread,
May 21, 2017, 5:27:11 AM5/21/17
to
> … it involves
> • Two vectors of equal size, one hold Person items and the other holding
> Person2 items. I.e. the latter is not empty.
> • The `age` values in the latter should be /changed/.

It is just an example but not real-world code.

JiiPee

unread,
May 21, 2017, 7:30:52 AM5/21/17
to
Ok thanks, I ll save this. Yes I see its too "heavy"in this situation.
Maybe better on other situations. But anyway good to know how to do it.

JiiPee

unread,
May 21, 2017, 8:17:49 AM5/21/17
to
On 21/05/2017 06:02, Alf P. Steinbach wrote:
> []( Person const& p, Person2 p2 ) { p2.age = p.age; return p2; }


But does this make first a copy of the old p2 and then modify that new
object and then assign it to the old object?

JiiPee

unread,
May 21, 2017, 4:34:47 PM5/21/17
to
Sure, but the point was here to find a std function for this.


On 21/05/2017 14:07, Stefan Ram wrote:
> JiiPee <n...@notvalid.com> writes:
> You can define you own algorithm to get a less contrived
> call.
>
> The following algorithm supplies an additional iterator
> called »reference« for a target which is to be modified:
>
> template
> < typename InputIterator, typename Function, typename Iterator >
> Function for_each
> ( InputIterator first, InputIterator last,
> Iterator reference, Function f )
> { for (; first != last; ++first)f( *first, reference++ );
> return ::std::move( f ); }
>
> . I am not used to write algorithms and so I'd gladly
> accept any criticism of the definition. (For example,
> the names could be improved.)
>
> This algorithm then can be called as in:
>
> for_each
> ( ::std::cbegin( vector0 ),
> ::std::cend( vector0 ),
> ::std::begin( vector1 ), /* target */
> []( person0 const & source, auto target_iterator )
> { target_iterator->age = source.age; } );
>
> .
>

JiiPee

unread,
May 22, 2017, 4:32:04 PM5/22/17
to
On 21/05/2017 06:02, Alf P. Steinbach wrote:
> transform(
> v.begin(), v.end(), // Source 1, for arg `p` below.
> v2.begin(), // Source 2, for arg `p2` below.
> v2.begin(), // Destination
> []( Person const& p, Person2 p2 ) { p2.age = p.age; return
> p2; }
> );


I tested this with a debugger.
It does make Person2 object copies (3 times). So it copies 3 times
Person2 objects to another Person2 objects. But originally I did not
want this neither (I think I could have solved this myself if object
copy was allowed). Copying is an expensive operation , so I dont think
this is a good solution compared to the best solution (only copying the
age values). Obviously the lambda has to return an object which is then
copied, right? So i guess its not possible to change/modify/copy *only*
the age values.


In your example you do copy also name height and name values (although
they are the original values - no none of thos evalues is changed..
copied but no value change).

JiiPee

unread,
May 22, 2017, 4:53:16 PM5/22/17
to
On 21/05/2017 06:02, Alf P. Steinbach wrote:
> []( Person const& p, Person2 p2 ) { p2.age = p.age; return p2; }


another thing is that cant you just do:

[](Person const& p, Person2& p2) { p2.age = p.age; return p2; }


so using a reference. I get the same result, and there is one object
copy less then.

Alf P. Steinbach

unread,
May 22, 2017, 4:58:36 PM5/22/17
to
Yes we can, as Obama said. But you don't get rid of the final copying
except as an optimization performed by the compiler. In contrast, when
you drop the restriction of using standard library functionality only,
then you can get both efficiency, conciseness, safety and clarity. ;-)


Cheers!,

- Alf

JiiPee

unread,
May 22, 2017, 5:03:08 PM5/22/17
to
On 22/05/2017 21:58, Alf P. Steinbach wrote:
> then you can get both efficiency, conciseness, safety and clarity. ;-)


totally agree :). Yes I can see that is the way in this example. Now
going to do that in my real code.... :)

Ye true in this case its more clear. Bjarne would definitely also say
that.... he supports simple solutions.

JiiPee

unread,
May 22, 2017, 5:04:05 PM5/22/17
to
On 22/05/2017 21:58, Alf P. Steinbach wrote:
> Yes we can, as Obama said.

haha

so how would Trump answer?

Gert-Jan de Vos

unread,
May 24, 2017, 12:31:04 PM5/24/17
to
-Wall

0 new messages