Question

I would like to remove elements from a vector using remove_if function but limiting the erasing to N elements.

Example:

// predicate function that determines if a value is an odd number.
bool IsOdd (int i) {

  if (we deleted more than deleteLimit)
    return false;

  return ((i%2)==1);
}


void otherFunc(){
  int deleteLimit = 10;

  // remove odd numbers:                       
  std::vector<int>::iterator newEnd =
  std::remove_if (myints.begin(), myints.end(), IsOdd (how to pass deleteLimit?) );
}

I need that IsOdd predicate stores how many elements it has removed and how many we want to delete. The only way is to use a global variable? Like this:

int deleteLimit = 10;
int removedSoFar = 0;
bool IsOdd (int i) {

  if (deleteLimit < removedSoFar)
    return false;

  if (i%2==1) {
    removedSoFar++
    return true;
  }

  return false;

}
remove_if ...
Was it helpful?

Solution

The state telling "how many elements have been removed so far" should be defined outside of the function / the call to the algorithm. This is because a functor should not have a state which is modified when being called (this would be undefined behavior).

You should take a reference to this state (counter) in the constructor of the functor (or capture by reference in the lambda), so you can access and modify this counter. When this functor is now copied, it doesn't matter which one is called by the algorithm, since all of them now hold the reference to the same state.

Using functors (C++03):

class IsOdd {
    int deleteLimit;
    int & deletedSoFar;

public:
    IsOdd(int deleteLimit, int & deletedSoFar) :
        deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
    {}

    bool operator()(int i) const {
        if (deletedSoFar < deleteLimit && i % 2) {
            ++deletedSoFar;
            return true;
        }
        return false;
    }
};

int deletedSoFar = 0;
int deleteLimit = 10;
std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));

Using lambda (C++11):

int deletedSoFar = 0;
int deleteLimit = 10;
auto it = std::remove_if (myints.begin(), myints.end(), [deleteLimit,&deletedSoFar](int i){
    if (deletedSoFar < deleteLimit && i % 2) {
        ++deletedSoFar;
        return true;
    }
    return false;
});
myints.erase(it, myints.end());

OTHER TIPS

Besides creating your own functor, you can pass a lambda expression:

auto deleteLimit = 25;
auto removedSoFar = 0;
auto it = remove_if (myints.begin(), 
                     myints.end(), 
                     [deleteLimit, &removedSoFar](int i)->bool 
                     { 
                       if ( (deletedSoFar < deleteLimit) && (i % 2)) {
                          ++deletedSoFar;
                          return true;
                       }
                       return false;
                      } );

// really remove the elements from the container
myints.erase(it, myints.end());

However, beware that stateful functors and std library algorithms are not always good mix. Here, calls to the lambda can have a side effect, so you have no guarantee over which elements in the sequence will be removed.

Note the final call to std::vector::erase. This is required to really remove the unwanted elements from the container. See the erase remove idiom.

int initial_size = myints.size();
std::remove_if (myints.begin(), myints.end(), [myints.size(),limit,initial_size](int i)->bool{  
return ( ( initial_size-myints.size() )<limit ? i%2 : false) } );

Use functor, structure with operator (), or use some bind features (std::bind/boost::bind).

bool isOdd(int current, int limit, int& count)
{
   if (count >= limit)
   {
      return false;
   }
   if (current % 2 == 1)
   {
      ++count;
      return true;
   }
   return false;
}

int count = 0, limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
std::bind(&isOdd, _1, limit, std::ref(count)), vec.end());

And with functor

struct IsOdd : public std::unary_function<int, bool>
{
public:
   IsOdd(int l) : count(0), limit(l)
   {
   }
   bool operator () (int i)
   {
      if (count >= limit)
      {
         return false;
      }
      if (current % 2 == 1)
      {
         ++count;
         return true;
      }
      return false;
private:
   int count;
   int limit;
};

int limit = 10;
vec.erase(std::remove_if(vec.begin(), vec.end(),
isOdd(limit)), vec.end());

The code in the voted up answer has a small mistake. There are missing parentheses just before the operator keyword. I couldn't make the edit as its less than six characters and I couldn't comment as my score is too low.

class IsOdd {
        int deleteLimit;
        int & deletedSoFar;

    public:
        IsOdd(int deleteLimit, int & deletedSoFar) :
            deleteLimit(deleteLimit), deletedSoFar(deletedSoFar)
        {}

        bool operator()(int i) const {
            if (deletedSoFar < deleteLimit && i % 2) {
                ++deletedSoFar;
                return true;
            }
            return false;
        }
    };

    int deletedSoFar = 0;
    int deleteLimit = 10;
    std::remove_if (myints.begin(), myints.end(), IsOdd(deleteLimit, deletedSoFar));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top