Using Erase-Remove_If Idiom

Using erase-remove_if idiom

The correct code is:

stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());

You need to remove the range starting from the iterator returned from std::remove_if to the end of the vector, not only a single element.

"Why?"

  • std::remove_if swaps elements inside the vector in order to put all elements that do not match the predicate towards the beginning of the container. This means that if the predicate (body of the lambda function) returns true, then that element will be placed at the end of the vector.

  • remove_if then **returns an iterator which points to the first element which matches the predicate **. In other words, an iterator to the first element to be removed.

  • std::vector::erase erases the range starting from the returned iterator to the end of the vector, such that all elements that match the predicate are removed.


More information: Erase-remove idiom (Wikipedia).

erase-remove_if idiom - was anything removed?

One idea would be to store the return value of std::remove_if, and compare it with the containers end() iterator before doing the erase like this:

bool removeMultiples(int multiple)
{
auto it = std::remove_if(v.begin(), v.end(), [multiple](int i){return i%multiple == 0;});
bool any_change = it != v.end();
v.erase(it, v.end());
return any_change;
}

How to erase value from vector of struct using erase-remove idiom?

I guess what you want to do is to remove all objects that have k==0, so create a lambda for that:

tabu.erase(
std::remove_if(tabu.begin(), tabu.end(),[](const tabuRecord& t){return t.k == 0;}),
tabu.end());

std::remove cannot work because it's not one value that you want to remove, but all values with a specific pattern, which is what std::remove_if does.

Improvement to remove-erase idiom when order need not be preserved

Your assumption about how remove_if works might be wrong. Perhaps you should state it explicitly.

Basically remove_if moves each not deleted element at most once, so it is particularly fast if most elements are being deleted. (It might optimise by first scanning over the initial part of the array which is not being deleted, in which case it will also be fast if few elements are being deleted and the first deleted element is near the end.)

Your swap algorithm does one swap for each element being deleted, so it is fastest if few elements are being deleted. But swap is unnecessary, and unnecessarily slow in some cases since it requires three moves. You could just move the last element over top of the element being deleted, possibly saving two data copies.

Why erase-remove idiom not working for reverse iterator

You miss-placed the calls to base(). remove_if will move all spaces it found starting from the end to the beginning part of the vector (as it would move spaces found starting from the beginning move towards the end if forward iterators were used) and returns the iterator pointing to the end position of the to-be-erased sequence (i.e. the begin of the space to be kept, as we reversed the iterator meanings), i.e.:

" ", " ", " ", "B", " ", "D", "E"

Then, you have to erase from the beginning, i.e. rend().base().

vec.erase(vec.rend().base(), 
std::remove_if(vec.rbegin(), vec.rend(), removeSpaceFromLast).base()
);

Crash when using erase–remove idiom

This line of code:

v.erase((std::remove(v.begin(), v.end(), v2[2]), v.end()));

is the equivalent of this:

v.erase((some_iterator, v.end()));

where some_iterator is the return value of the call to std::remove.

That now becomes the equivalent of:

v.erase(v.end());

The reason why is that the parentheses enclosed this statement:

(some_iterator, v.end())

into an expression that invokes the comma operator. The comma operator takes the value on the right of the comma, and uses that as the final value.

The correction is to remove the excessive parentheses:

v.erase(std::remove(v.begin(), v.end(), v2[2]), v.end());

Is there a way to use the erase-remove idiom in concert with other looping conditions?

If you don't want to do two loops because you've measured and found that it's slower, write a custom algorithm:

template <typename Iter, typename OutIter>
OutIter lowercased_without_punctuation(Iter begin, Iter end, OutIter out) {
while (begin != end) {
// Ignoring things like std::move_iterator for brevity.
if (!is_punctuation(*begin)) {
*out = tolower(*begin);
++out;
}

// Use `++iter` rather than `iter++` when possible
++begin;
}

return out;
}

// ...

string encipher(string text) {
Alphabet a;
encipheredAlphabet = a.cipherLetters(keyword);

text.erase(
lowercased_without_punctuation(text.begin(), text.end(), text.begin()),
text.end());

return text;
}

If you think about it some more, lowercased_without_punctuation is actually a special-case of a more general algorithm which might be called transform_if (relevant Q&A):

template <typename Iter, typename OutIter, typename Pred, typename Transf>
OutIter transform_if(Iter begin, Iter end, OutIter out, Pred p, Transf t) {
while (begin != end) {
if (p(*begin)) {
*out = t(*begin);
++out;
}

++begin;
}

return out;
}

// ...

string encipher(string text) {
Alphabet a;
encipheredAlphabet = a.cipherLetters(keyword);

text.erase(
transform_if(text.begin(), text.end(), text.begin(),
[](char c) { return !is_punctuation(c); },
[](char c) { return tolower(c); }),
text.end());

return text;
}

Erase remove idiom on a std::string with non-printable characters throws exception

If the data in your file may contain nulls (amidst other characters that you're interested in), or it is not guaranteed to have a null at the very end, then you need to tell std::string how much data it should use to build the string. You do this with the std::string( const char *, size_t ) constructor:

std::string  s( chBuf, dwRead );

The string cleaning process should work OK if you do that.



Related Topics



Leave a reply



Submit