1

Which algorithm or combine of algorithms can use for following situation?

struct Term
{
    int ix;
    double f;
};

std::vector<Term> terms = <intitalize terms>;
std::vector< int > termIxVector;

// NEED get all `ix` from the `terms` where term.f < 1.0, 
   // and insert 'ix' result to termIxVector.
//i.e. equavalent this loop:
for(std::size_t i = 0; i < terms.size(); ++i)
    if ( terms[i].f < 1.0 )
             termIxVector.push_back(terms[i].ix);

std::copy_if copies only Term structure. std::transform - doesn't support predicate.

Khurshid
  • 2,654
  • 2
  • 21
  • 29
  • 2
    A plain (range-based) for loop should be plenty enough. Or `std::for_each` if you really want an ``. Does that not suit you? – Mat May 24 '14 at 06:07

3 Answers3

3

Use std::for_each with a lambda

std::for_each(terms.begin(), terms.end(), 
              [&termIxVector](Term const& t) { 
                  if(t.f < 1.0) termIxVector.push_back(t.ix); 
              });

Or a range based for

for(auto const& t : terms) {
  if(t.f < 1.0) termIxVector.push_back(t.ix);
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Sorry, but I'm use Visual Studio 2010 which doesn't support lambda with capture (simple lambda can), and range based 'for' also can't support. Couldn't You show without lambda and range-base 'for' way ? I'm more interesting combination of algorithms. – Khurshid May 24 '14 at 06:21
  • 2
    VS2010 supports lambda with capture, we use them all the time. – Chad May 24 '14 at 06:25
  • 1
    @Khurshid What error are you getting from VS2010? I don't have it installed anymore, but I seem to remember lambdas that capture variables working just fine on it. What if you don't explicitly name the variable you're capturing? `[&](Term const& t) { ... }` – Praetorian May 24 '14 at 06:25
  • @Praetorian: VS2010 - support lambda capture by reference only, explicit capture by value doens't -- in common VS2010 100% not support C++11 lambda. – Khurshid May 24 '14 at 06:38
  • @Khurshid I have no idea what you're going on about. I am capturing the vector by reference, not by value. And, once again, I'm pretty sure VS2010 supports both. If I remember correctly, the only thing it doesn't support is implicit conversion of a capture-less lambda to a function pointer. Try the code, and post any errors you see. And there is no other *interesting combinations* of algorithms to be used here. You only need the one - `for_each`. If you don't *want to use* a lambda, you can create a functor by hand, but it's essentially the same as the lambda. – Praetorian May 24 '14 at 06:49
  • @Praetorian. Thanks. I googled some transform_if algorithm, but not found. – Khurshid May 24 '14 at 07:02
  • @Praetorian +1 because it works, but Boost.Range ftw, see my answer. – TemplateRex May 25 '14 at 08:12
1

These kind of compositions of elementary algorithms are most conveniently done with Boost.Range:

#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <iostream>
#include <iterator>
#include <vector>

struct Term
{
    int ix;
    double f;
};

int main()
{
    using namespace boost::adaptors;

    auto terms = std::vector<Term>{ {0, 0.1}, {1, 1.1}, {2, 0.8}, {3, 1.2}, {4, 0.9} };
    auto termIxVector = terms 
        | filtered([](auto const& x){ return x.f < 1.0; }) // select
        | transformed([](auto const& y){ return y.ix; })   // project
    ;    

    boost::copy(termIxVector, std::ostream_iterator<int>(std::cout, ","));    
}

Live Example using Clang 3.5 SVN which prints the indices 0, 2, and 4 of your Term elements having a double less than 1.0.

NOTE: these ranges are lazily computed, and termIxVector is not a std::vector<int> actually. If you want that, you need to do something like

std::vector<int> termIxVector;
boost::push_back(termIxVector, /* RHS of the same range composition as above */);
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
0

If you really want to use std::copy_if, then an idea is to overload operator int() in the struct and use the standard std::copy_if with a unary predicate (lambda or functor), as this will allow you to convert Term to int. Code below:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

struct Term{
    int ix;
    double f;
    operator int(){
        return ix;
    }
};

struct predicate{ // if you don't want a lambda
    bool operator()(const Term& t){
        return (t.f < 1.0 ? true : false);
    }
};

int main()
{
    std::vector<Term> terms = {{1, 0.1}, {2, 0.2}, {3, 1.1}, {4, 0.9}};
    std::vector< int > termIxVector;

    std::copy_if(terms.begin(), terms.end(), 
                 back_inserter(termIxVector), predicate());

    for(auto& elem: termIxVector)
        std::cout << elem << std::endl;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252