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) returnstrue
, 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
Is There Any Guarantee of Alignment of Address Return by C++'s New Operation
When and How to Use Exception Handling
How to Cin Values into a Vector
How to Get a Random Element from a C++ Container
Convert Std::Bind to Function Pointer
Create a Function to Check for Key Press in Unix Using Ncurses
Why Should I Use the "Using" Keyword to Access My Base Class Method
Are Destructors Called After a Throw in C++
Is There a Formula to Determine Overall Color Given Bgr Values? (Opencv and C++)
How to Use Std::String in a Constexpr
Faster Bulk Inserts in SQLite3
How to Remove Constness of Const_Iterator