This question already has answers here:
Erasing elements from a vector
(6 answers)
Closed 8 years ago.
Here my code. I want remove from vector all elements with successfully called method 'release'.
bool foo::release()
{
return true;
}
// ...
vector<foo> vec;
// ...
remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });
// ...
But remove_if not deleting all elements from vector vec. How remove_if works?
std::remove_if re-arranges the elements of the vector such that the elements you want to keep are in the range [vec.begin(), return_iterator) (note the partially open range). So you need to call std::vector::erase to make sure the vector contains only the desired elements. This is called the erase-remove idiom:
auto it = remove_if(vec.begin(),
vec.end(),
[](foo & f) { return f.release() == true; });
vec.erase(it, vec.end());
Here, I have split it into two lines for clarity, but it is often seen as a one-liner.
std::remove and std::remove_if do not actually remove anything but just give you an iterator by which you can then erase elements using the appropriate member function of whatever container you use. In std::vector's case, erase.
I invite you to read this old article from Scott Meyers: "My Most Important C++ Aha! Moments...Ever":
It was thus with considerable shock and a feeling of betrayal that I discovered that applying remove to a container never changes the number of elements in the container, not even if you ask it to remove everything. Fraud! Deceit! False advertising!
Because the remove_if algorithm operates on a range of elements denoted by two forward iterators, it has no knowledge of the underlying container or collection.
Thus, no elements are actually removed from the container. Rather, all elements which don't fit the remove criteria are brought together to the front of the range, in the same relative order.
The remaining elements are left in a valid, but unspecified, state. When this is done, remove returns an iterator pointing one element past the last unremoved element.
To actually eliminate elements from the container, remove should be combined with the container's erase member function (hence the name "erase-remove idiom").
How to use remove-erase idiom for removing empty vectors in a vector?
Erasing elements from a vector
See http://en.wikipedia.org/wiki/Erase-remove_idiom
std::remove_if doesn't actually obliterate erase the elements. What it does is move the elements that satisfies the criteria into the end of the range. It then returns an iterator to the first element of the removed (which are actually just moved) elements. It is on you then to erase that range from the container.
vector<foo> vec;
auto remove_start = remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });
vec.erase(remove_start, vec.end());
or
vec.erase(remove_if(vec.begin(), vec.end(),
[](foo & f) { return f.release() == true; }),
vec.end());
Related
Lets suppose I have a unsorted list of iterators to delete defined as
std::vector<std::vector<int>::iterator> _unsortedIterList;
Of another vector defined as:
std::vector<int> _listValues;
Then this code will not work (since erasing will invalidate the remaining iterators).
for ( auto it: _unsortedIterList)
{
_listValues.erase(it);
}
Is there a way to erase all of the iterators properly?
Erasing invalidates iterators at or after the point of erase. So all you have to do is ensure that we erase back to front:
// because random access iterators are comparable
std::sort(_unsortedIterList.begin(), _unsortedIterList.end(),
std::greater<>{});
// now this is back-to-front, so each erase will keep every other iterator valid
for (auto it : _unsortedIterList) {
_listValues.erase(it);
}
I would like to know what's the best practice to remove an element from a vector in C++.
I have seen many times people using std::remove to find and delete the element, and then using erase to remove the element from the vector.
But why is it better than using find to get the iterator of the element you want to remove and then using the erase with that iterator?
Thanks
std::find followed by vector::erase will erase the first occurrence of an object with the given value from the vector.
std::vector<int> vec{1,3,3,8,3,5};
vec.erase(std::find(vec.begin(), vec.end(), 3));
//vec == {1,3,8,3,5}
std::remove followed by vector::erase will remove every occurrence of an object with the given value from the vector.
std::vector<int> vec{1,3,3,8,3,5};
vec.erase(std::remove(vec.begin(), vec.end(), 3), vec.end());
//vec == {1,8,5}
Neither is better, they just do different things.
std::remove is more generally useful, and that is why it is more often seen; in particular, std::remove followed by vector::erase does nothing when the element is not present in the vector, while std::find followed by vector::erase has undefined behavior.
Note that both "find-erase", "remove-erase" maintain the relative order of the elements. If you want to remove an element from the vector but do not care about the resulting order of the elements, you can use "find-move-pop_back" or "partition-erase":
//find-move-pop_back
std::vector<int> vec{1,3,3,8,3,5};
*std::find(vec.begin(), vec.end(), 3) = std::move(vec.back());
vec.pop_back();
//partition-erase
std::vector<int> vec{1,3,3,8,3,5};
vec.erase(
std::partition(vec.begin(), vec.end(), [](int v){return v != 3;}),
vec.end());
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C++ Delete Duplicate Entries in a vector
I need to delete double entries in C++ STL vectors. An important point is that the order of elements in the resulting vector must be equivalent to the order in the input vector. Is there an algorithm (e.g. in stl, boost) which would do this?
There are two possible cases here: either the vector is already sorted or it isn't.
If it is, std::erase and std::unique can easily solve this as shown in the other answers.
If it isn't then you can do achieve the goal with
v.erase(std::remove_if(v.begin(), v.end(), predicate), v.end());
but there's a problem in that predicate is not trivial to specify: it's a function that accepts one argument (the value to consider) and it needs to answer the question "is there any equal value earlier in the vector?". Since you aren't told where exactly in the vector the supplied argument is, that means you 'd have to keep quite a bit of manual state to be able to answer this.
A convenient option here would be to use an std::set to do some of the heavy lifting:
std::set<decltype(v)::value_type> set(v.begin(), v.end());
v.erase(
std::remove_if(
v.begin(),
v.end(),
[&set] (decltype(v)::value_type item) { return !set.erase(item); }),
v.end());
What this does is prepopulate an std::set with the values in the vector and then check if an item has been seen before by seeing if it has been removed from the set. This way the result will retain only the first item from each set of items that compare equal in the input.
See it in action.
If your vector is not sorted and you thus cannot just use std::unique (and likewise cannot sort it which would destroy your order), you can use something like this function (using C++11 lambdas):
template<typename FwdIt> FwdIt unordered_unique(FwdIt first, FwdIt last)
{
typedef typename std::iterator_traits<FwdIt>::value_type value_type;
std::set<value_type> unique;
return std::remove_if(first, last, [&unique](const value_type &arg) {
return !unique.insert(arg).second; });
}
Which can be invoke using the usual erase-romve-idiom:
v.erase(unordered_unique(v.begin(), v.end()), v.end());
Of course you can also use C++11's std::unordered_set instead of a std::set (for hashable types, of course) to get away from O(n log n) in average case.
How about std::unique?
auto firstDup = std::unique(myvector.begin(), myvector.end());
Use next:
vec.erase( unique( vec.begin(), vec.end() ), vec.end() );
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 index, and if a certain condition is true, I want to delete that element.
How do I delete a vector element while looping over it without crashing?
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.
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());
Use the Erase-Remove Idiom, using remove_if with a predicate to specify your condition.
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).
I have an std::vector of objects and i cycle it calling some methods of the object. One of them will check a particular condition and if needed will delete itsef from the vector. The point is that erasing the element invalidate the iterator and I cannot continue the loop of it. I found boost::shared_ptr and boost::weak_ptr, could these fix the problem deleting the object after calling all methods and after incremented the iterator? If so, how?
EDIT 1
class CPippo
{
public:
void Pippo();
void Pippo2();
}
class CPippoManager
{
public:
void PipppManager();
void RemovePippo(CPippo *pippo);
private:
std::vector<CPippo*> pippoVector;
}
void CPippo::Pippo()
{
...
if (condition)
{
pippoManager->RemovePippo(this);
}
}
void CPippo::Pippo2()
{
...
}
void CPippoManager::RemovePippo(CPippo *pippo)
{
this->pippoVector.erase(this->pippoVector.begin(), this->pippoVector.end(), pippo);
}
void CPippoManager::PipppManager()
{
for (std::vector<CPippo*>::iterator it = this->pippoVector.begin(); it != this->pippoVector.end; ++it)
{
(*it)->Pippo();
(*it)->Pippo2();
}
}
Never mind what your vector contains - the deletion of managed resources can indeed be left to smart pointers, but the more pressing issue is how to manipulate the container itself.
std::vector indeed has very poor iterator invalidation: erasing or inserting invalidates all iterators from the erasee/insertee onwards, so you cannot even use the standard earase(it++) idiom. But neither should you, since erasing from a vector is expensive. The better solution is to use remove/erase and supply a functor that checks the condition for erasure, and then erase everything in one wash:
std::vector<T> v;
v.erase(std::remove_if(v.begin(), v.end(), MyPred), v.end());
Here MyPred is a predicate that implements your criterion. In C++11 this could be a handy lambda.
If your existing algorithm is too involved, perhaps you can adapt the idea of remove in your own algorithm and move the to-be-erased object to the back of the vector with swap, and return the iterator past the last good element at the end of the algorithm. Then you can have your own optional clean-up loop on the range of objects to be deleted, and then call erase on that range.
As you mention calling a member function you should the erase/remove idiom with std::mem_fun_ref
v.erase(std::remove_if(v.begin(), v.end(), std::mem_fun_ref(&Class::function), v.end()));
To safely delete elements from a vector you should use the erase-remove idiom.
EDIT: Lots of good responses posted before mine, so thought I'd add a little extra clarification on remove because it's not immediately obvious how it operates. The remove algorithm doesn't actually remove anything. All it does is shuffle the container such that all the elements to keep are at the beginning of the container. It then returns an iterator to the end of the kept elements (one past the last element to keep) and that's when you use erase to actually shorten the container. For example:
vector v;...
v.erase(remove(v.begin(), v.end(), 99), v.end());
will remove all elements in v with the value 99.
Use the erase-remove idiom:
// You can write a function, a functor or use lambda directly it is up to you!
bool condition(const vector<object>& v);
v.erase( remove_if(v.begin(), v.end(), condition), v.end() );
This is a fantastic article talking about the subject of writing algorithms involving iterators.
You can get a valid iterator back from erase().
for(... it = vec.begin(); it != vec.end(); it++) {
if (condition(it))
it = --vec.erase(it);
}
Of course, you should always use smart pointers to manage resources.