I am looping through a vector with a loop such as for(int i = 0; i < vec.size(); i++)
. Within this loop, I check a condition on the element at that vector i
The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred
and then:
static bool pred( const std::string &s ) {
// ...
}
std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );
If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:
std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}
However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.
Use the Erase-Remove Idiom, using remove_if
with a predicate to specify your condition.
If you cannot use remove/erase (e.g. because you don't want to use lambdas or write a predicate), use the standard idiom for sequence container element removal:
for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
if (delete_condition)
{
it = v.erase(it);
}
else
{
++it;
}
}
If possible, though, prefer remove/erase:
#include <algorithm>
v.erase(std::remove_if(v.begin(), v.end(),
[](T const & x) -> bool { /* decide */ }),
v.end());
if(vector_name.empty() == false) {
for(int i = vector_name.size() - 1; i >= 0; i--)
{
if(condition)
vector_name.erase(vector_name.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
Iterate over the vector backwards. That way, you don't nuke the ability to get to the elements you haven't visited yet.
I realize you are asking specifically about removing from vector, but just wanted to point out that it is costly to remove items from a std::vector since all items after the removed item must be copied to new location. If you are going to remove items from the container you should use a std::list. The std::list::erase(item) method even returns the iterator pointing to the value after the one just erased, so it's easy to use in a for or while loop. Nice thing too with std::list is that iterators pointing to non-erased items remain valid throughout list existence. See for instance the docs at cplusplus.com.
That said, if you have no choice, a trick that can work is simply to create a new empty vector and add items to it from the first vector, then use std::swap(oldVec, newVec), which is very efficient (no copy, just changes internal pointers).