Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iterate vector, remove certain items as I go

I have a std::vector m_vPaths; I will iterate this vector and call ::DeleteFile(strPath) as I go. If I successfully delete the file, I will remove it from the vector. My question is can I get around having to use two vectors? Is there different data structure that might be better suited for what I need to do?

example: using iterators almost does what I want, but problem is once you erase using an iterator, all iterators become invalid.

 std::vector<std::string> iter = m_vPaths.begin();     for( ; iter != m_vPaths.end(); iter++) {         std::string strPath = *iter;         if(::DeleteFile(strPath.c_str())) {             m_vPaths.erase(iter);                    //Now my interators are invalid because I used erase,                 //but I want to continue deleteing the files remaining in my vector.             }     } 

I can use two vectors and I will no longer have a problem, but is there a better, more efficient method of doing what I'm trying to do?

btw, incase it is unclear, m_vPaths is declared like this (in my class):

std::vector<std::string> m_vPaths; 
like image 965
cchampion Avatar asked Oct 22 '09 01:10

cchampion


People also ask

How do I remove a specific value from a vector?

The erase() function can remove an element from the beginning, within, or end of the vector. In order to remove all the elements from the vector, using erase(), the erase() function has to be repeated the number of times there are elements, beginning from the first element.

How do I remove multiple elements from a vector file?

If you need to remove multiple elements from the vector, the std::remove will copy each, not removed element only once to its final location, while the vector::erase approach would move all of the elements from the position to the end multiple times.


2 Answers

The erase() method returns a new (valid) iterator that points to the next element after the deleted one. You can use this iterator to continue with the loop:

std::vector<std::string>::iterator iter; for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {     if (::DeleteFile(iter->c_str()))         iter = m_vPaths.erase(iter);     else         ++iter; } 
like image 97
sth Avatar answered Sep 20 '22 18:09

sth


Check out std::remove_if:

#include <algorithm> // for remove_if #include <functional> // for unary_function  struct delete_file : public std::unary_function<const std::string&, bool>  {     bool operator()(const std::string& strPath) const     {         return ::DeleteFile(strPath.c_str());     } }  m_vPaths.erase(std::remove_if(m_vPaths.begin(), m_vPaths.end(), delete_file()),                 m_vPaths.end()); 

Use a std::list to stop the invalid iterators problem, though you lose random access. (And cache performance, in general)


For the record, the way you would implement your code would be:

typedef std::vector<std::string> string_vector; typedef std::vector<std::string>::iterator string_vector_iterator;  string_vector_iterator iter = m_vPaths.begin(); while (iter != m_vPaths.end()) {     if(::DeleteFile(iter->c_str()))     {         // erase returns the new iterator         iter = m_vPaths.erase(iter);     }     else     {         ++iter;     } } 

But you should use std::remove_if (reinventing the wheel is bad).

like image 31
GManNickG Avatar answered Sep 20 '22 18:09

GManNickG