Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

create container from another container, applying each element some function in c++

My question is simple, see example:

std::array<int,6> a = {{0,1,2,3,4,5}}; // -- given container.
auto F = []( int i ) { return  i*i; }; // -- given function.

std::vector<int> v;  // need create

// my solution:
v.reserve( a.size () );
for( std::size_t i = 0; i < a.size(); ++i )
    v.push_back( F(a[i]) ); 



// but I need something like
   std::vector<int>v( a.begin(), a.end(), <|applying each element to F|> );

Can I create container something like above not calling reserve explicitly and any reallocation?

EDIT:

  1. I want avoid reserve; because othercase my first solution is good for me :)
  2. I want avoid any resize; because it's initialzed each element by default ctor.
  3. I will use this in the real project which may included many 3-rd party libraries ( boost, Soft-STL, ...).
like image 546
Khurshid Avatar asked Feb 21 '14 19:02

Khurshid


3 Answers

The standard algorithm std::transform does exactly this!

std::vector<int> v(a.size());
std::transform(
   std::begin(a), std::end(a),
   std::begin(v),
   F
);

You can start with an empty vector and use std::back_inserter, if you like:

std::vector<int> v;
std::transform(
   std::begin(a), std::end(a),
   std::back_inserter(v),
   F
);

But you're subjecting yourself to needless re-allocations if you do that (unless you reserve first, as in your original attempt). You can decide for yourself what your priority is.

like image 140
Lightness Races in Orbit Avatar answered Oct 21 '22 04:10

Lightness Races in Orbit


Use std::transform:

#include <algorithm> // std::transform
#include <iterator>  // std::back_inserter

....

transform(a.begin(), a.end(), back_inserter(v), F);

You may want to call v.reserve(asize()) first to avoid re-allocations.

like image 45
juanchopanza Avatar answered Oct 21 '22 04:10

juanchopanza


Another solution is to use boost::transform_iterator. The benefit is that you can pass iterators to the container constructor. That avoids memory reallocations compared to when using std::back_inserter or having to call reserve or resize on the destination. All in one statement:

std::vector<int> result(
      boost::make_transform_iterator(std::begin(a), F)
    , boost::make_transform_iterator(std::end(a), F)
    );

You can achieve terser syntax though, like this:

std::vector<int> result(transform_range(a, F));

transform_range implementation:

template<class Iterator>
struct AutoSequence
{
    Iterator const beg_, end_;

    template<class T>
    operator std::vector<T>() const {
        return {beg_, end_};
    }
};

template<class Function, class InSeq>
auto transform_range(InSeq const& in) -> AutoSequence<decltype(boost::make_transform_iterator<Function>(in.begin()))> {
    return {
          boost::make_transform_iterator<Function>(std::begin(in))
        , boost::make_transform_iterator<Function>(std::end(in))
        };
}

template<class Function, class InSeq>
auto transform_range(InSeq const& in, Function&& f) -> AutoSequence<decltype(boost::make_transform_iterator(in.begin(), f))> {
    return {
          boost::make_transform_iterator(std::begin(in), f)
        , boost::make_transform_iterator(std::end(in), f)
        };
}
like image 24
Maxim Egorushkin Avatar answered Oct 21 '22 05:10

Maxim Egorushkin