HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

C++ idiom for selecting integers from a vector according to predetermined properties

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
propertiesidiomintegerspredeterminedforselectingfromaccordingvector

Problem

I want a function Select that takes a vector and a condition (such as being even, being divisible by three, being larger than 4, or any logical combination thereof), and returns another vector composed of the members of the original vector satisfying the condition.

For example Select(v, IsEven) should return the even members of v.

Select(v, !IsEven && LessThan5) should return odd members of v that are less than 5.

And for the final example Select(v, IsEven || !LessThan5) should return elements of v that are either even or greater or equal to 5.

It is important that the conditions are represented by names rather than logical expressions. In other words I prefer Select(v, IsEven) to Select(v, [](int x)->bool{return x%2==0;}). This is because I expect to use the same properties multiple times and would rather not make mistakes copying and pasting. I also think it makes the code more readable.

I could write boolean functions for the basic conditions, and for the composite statements make use of lambdas i.e. Select(v, [](int x){ return !IsEven(x) && LessThan5(x);}). But again I think this still looks ugly, and I expect to use these Select functions repeatedly through the code so I would prefer to type less.

I came up with the solution of using function objects derived from a common base class as seen below.

```
#include
#include

using namespace std;

//Condition Base and Logical Classes
class NotCondition;
class AndCondition;
class OrCondition;

class BaseCondition{
public:
BaseCondition(){}
virtual bool operator () (int x) const = 0;
friend const NotCondition operator !(const BaseCondition& cond);
friend const AndCondition operator &&(const BaseCondition& left, const BaseCondition& right);
friend const OrCondition operator ||(const BaseCondition& left, const BaseCondition& right);
};

class NotCondition : public BaseCondition{
public:
NotCondition(const BaseCondition& cond) : BaseCondition(), cond_{cond} {}
bool operator

Solution

Personally, I found your implementation to be really readable. I have no opinion about efficiency.

But, for me, you reimplement std::copy_if:

std::vector input{1, 2, 3, 4 5, ... };
std::vector result(input.size());

auto it = std::copy_if(input.begin(), 
                  input.end(),
                  result.begin(),
                  [](int i)->bool { return (i%2)==0; });
result.resize(std::distance(result.begin(), it));


You can encapsulate this one as you want to obtain the readability you want. Using this is more scalable, because you just have to rewrite condition in builtin functionality. You can also call function. Less code, less dependency, native bool operation, ...

constexpr bool isEven(int i) { return (i%2) == 0; }

template
std::vector Select(std::vector v, UnaryPredicate p)
{
    std::vector result(v.size());
    auto it = std::copy_if(v.begin(),
                  v.end(),
                  result.begin(),
                  p);
    result.resize(std::distance(result.begin(), it));
    return result;
}

Select(input, [](int i)->bool { return isEven(i) && i > 5 });


Or, without template

constexpr bool isEven(int i) { return (i%2) == 0; }

std::vector Select(std::vector v, std::function p)
{
    std::vector result(v.size());
    auto it = std::copy_if(v.begin(),
                  v.end(),
                  result.begin(),
                  p);
    result.resize(std::distance(result.begin(), it));
    return result;
}

Select(input, [](int i)->bool { return isEven(i) && i > 5 });


For safety, I prefer to use STL or Boost or common lib than reimplement by myself.

Code Snippets

std::vector<int> input{1, 2, 3, 4 5, ... };
std::vector<int> result(input.size());

auto it = std::copy_if(input.begin(), 
                  input.end(),
                  result.begin(),
                  [](int i)->bool { return (i%2)==0; });
result.resize(std::distance(result.begin(), it));
constexpr bool isEven(int i) { return (i%2) == 0; }

template<typename UnaryPredicate>
std::vector<int> Select(std::vector<int> v, UnaryPredicate p)
{
    std::vector<int> result(v.size());
    auto it = std::copy_if(v.begin(),
                  v.end(),
                  result.begin(),
                  p);
    result.resize(std::distance(result.begin(), it));
    return result;
}

Select(input, [](int i)->bool { return isEven(i) && i > 5 });
constexpr bool isEven(int i) { return (i%2) == 0; }

std::vector<int> Select(std::vector<int> v, std::function<bool(int)> p)
{
    std::vector<int> result(v.size());
    auto it = std::copy_if(v.begin(),
                  v.end(),
                  result.begin(),
                  p);
    result.resize(std::distance(result.begin(), it));
    return result;
}

Select(input, [](int i)->bool { return isEven(i) && i > 5 });

Context

StackExchange Code Review Q#121052, answer score: 6

Revisions (0)

No revisions yet.