Removing an item from STL::Vector

Started by
6 comments, last by Zahlman 17 years, 3 months ago
What is the fastest way to remove an element from the stl::vector while keeping the order of the arrangement? For example to erase the 3rd element, 'Array[2]', I use:

std::vector<int> Array;
std::vector<int>::iterator Iter;
Iter = Array.begin() + 2;
Array.erase(Iter);
Is there a faster and better way to go about doing it? Or am I doing it wrongly? Thanks.
Advertisement
I'm pretty sure thats the best approach.
Quote:Original post by littlekid
std::vector<int> Array;std::vector<int>::iterator Iter;Iter = Array.begin() + 2;Array.erase(Iter);


I don't see any particular reason to keep that temporary iterator around, unless you're going to be using it again. For this simple case, you could just say
Array.erase(Array.begin( ) + 2);
You can find more basic information on std::vectors here.

Moral of the story: there's nothing wrong with what you're doing, but you could simplify it for trivial cases. [smile]
-jouley

Jouley says it how it is.. ;)

Also, Here! has helped me throughout my vector endeavors... that sorta rhymes?
0) Like jouley said.

1) If you want to remove 'Array[2]', you'd better be sure the vector is at least that long first. :)

2) If you will remove several things at a time, you should instead try to make use of the standard library algorithms std::remove or std::remove_if. Each of these operates over a range, shuffling everything that *shouldn't* be removed to the beginning of the range, and leaving the contents of the rest of the range *undefined*. It will return an iterator pointing just past the end of the "unremoved" elements, which allows you to clean up by using the *free function* std::erase, which is another standard library algorithm that removes a range of elements from a container.

Put together, their use is called the "erase-remove idiom", and it looks like this:

std::vector<std::string> > serious_words;// Our boss just informed us that "warthog" is a silly word, so we// have to remove every occurrance of "warthog" from serious_words.// We do it like this:std::erase(std::remove(serious_words.begin(), serious_words.end(), "warthog"),           serious_words.end());// The remove() call gives an iterator one past the end of the remaining// serious words, which is therefore the beginning of the range we must erase.// Later, we have to remove all the words that have a 'q' in them.// First, we need a binary function - or "predicate" - that will tell us// whether a given string contains a q:bool has_q(const std::string& s) {  return s.find('q') != std::string::npos;}// Then we can use the remove_if form:std::erase(std::remove_if(serious_words.begin(), serious_words.end(), has_q),           serious_words.end());// This can also be made to work with functors, or with specially "adapted"// member functions using something like std::mem_fun_ref.
Quote:Original post by Zahlman
0) Like jouley said.

1) If you want to remove 'Array[2]', you'd better be sure the vector is at least that long first. :)

2) If you will remove several things at a time, you should instead try to make use of the standard library algorithms std::remove or std::remove_if. Each of these operates over a range, shuffling everything that *shouldn't* be removed to the beginning of the range, and leaving the contents of the rest of the range *undefined*. It will return an iterator pointing just past the end of the "unremoved" elements, which allows you to clean up by using the *member function* std::erase, which removes a range of elements from the container.

Put together, their use is called the "erase-remove idiom", and it looks like this:

std::vector<std::string> serious_words;// Our boss just informed us that "warthog" is a silly word, so we// have to remove every occurrance of "warthog" from serious_words.// We do it like this:serious_words.erase(std::remove(serious_words.begin(), serious_words.end(), "warthog"),           serious_words.end());// The remove() call gives an iterator one past the end of the remaining// serious words, which is therefore the beginning of the range we must erase.// Later, we have to remove all the words that have a 'q' in them.// First, we need a binary function - or "predicate" - that will tell us// whether a given string contains a q:bool has_q(const std::string& s) {  return s.find('q') != std::string::npos;}// Then we can use the remove_if form:serious_words.erase(std::remove_if(serious_words.begin(), serious_words.end(), has_q),           serious_words.end());// This can also be made to work with functors, or with specially "adapted"// member functions using something like std::mem_fun_ref.
Fixed. [smile]

Σnigma
Another thing to be aware of is that since std::vector stores its contents in contiguous memory, removing elements from anywhere except the end of a vector can be an expensive operation. If you need to do so often or you are storing elements that are expensive to copy you might be better off using a different container like a std::list (or possibly a std::deque).

Though as always, make the decision based on your needs and what profiling the code tells you.
Quote:Original post by Enigma
Quote:Original post by Zahlman
0) Like jouley said.

1) If you want to remove 'Array[2]', you'd better be sure the vector is at least that long first. :)

2) If you will remove several things at a time, you should instead try to make use of the standard library algorithms std::remove or std::remove_if. Each of these operates over a range, shuffling everything that *shouldn't* be removed to the beginning of the range, and leaving the contents of the rest of the range *undefined*. It will return an iterator pointing just past the end of the "unremoved" elements, which allows you to clean up by using the *member function* std::erase, which removes a range of elements from the container.

Put together, their use is called the "erase-remove idiom", and it looks like this:

std::vector<std::string> serious_words;// Our boss just informed us that "warthog" is a silly word, so we// have to remove every occurrance of "warthog" from serious_words.// We do it like this:serious_words.erase(std::remove(serious_words.begin(), serious_words.end(), "warthog"),           serious_words.end());// The remove() call gives an iterator one past the end of the remaining// serious words, which is therefore the beginning of the range we must erase.// Later, we have to remove all the words that have a 'q' in them.// First, we need a binary function - or "predicate" - that will tell us// whether a given string contains a q:bool has_q(const std::string& s) {  return s.find('q') != std::string::npos;}// Then we can use the remove_if form:serious_words.erase(std::remove_if(serious_words.begin(), serious_words.end(), has_q),           serious_words.end());// This can also be made to work with functors, or with specially "adapted"// member functions using something like std::mem_fun_ref.
Fixed. [smile]

Σnigma



Blaaaargh. You are correct, of course. I knew something was up... I was going to write on explaining about why remove(_if) can't affect the container size, and then deleted that when I saw that my 'erase' was affecting the container size... missing the obvious aren't I :(

This is one reason why I recommend wrapping the idiom in a function :)

// cause the container to not contain anything matching p.template <typename Container, typename Predicate>inline void filter_out(Container& c, Predicate p) {  c.erase(std::remove_if(c.begin(), c.end(), p), c.end());}// cause the container to *only* contain things matching p.// Raw function pointers won't work as predicates any more; adapt them// using std::ptr_fun first.template <typename Container, typename AdaptablePredicate>inline void filter(Container& c, AdaptablePredicate p) {  c.erase(std::remove_if(c.begin(), c.end(), std::not1(p)), c.end());}

This topic is closed to new replies.

Advertisement