77

I have a vector that holds items that are either active or inactive. I want the size of this vector to stay small for performance issues, so I want items that have been marked inactive to be erased from the vector. I tried doing this while iterating but I am getting the error "vector iterators incompatible".

vector<Orb>::iterator i = orbsList.begin();

    while(i != orbsList.end()) {
        bool isActive = (*i).active;

        if(!isActive) {
            orbsList.erase(i++);
        }
        else {
            // do something with *i
            ++i;
        }
    }
2
  • See this related question: stackoverflow.com/questions/347441/… Commented Jan 17, 2011 at 12:30
  • this sometimes invelidates iterator but this one works safe when digit is a vector and arr is an array for (auto a = digit.begin(); a != digit.end();) { for (auto b= arr.begin(); b != arr.end();) { if ((*a) == (*b)) { a = digit.erase(a); if(!(a==digit.begin())) --a; } ++b; } ++a; } Commented Nov 15 at 14:33

8 Answers 8

80

The most readable way I've done this in the past is to use std::vector::erase combined with std::remove_if. In the example below, I use this combination to remove any number less than 10 from a vector.

(For non-c++0x, you can just replace the lambda below with your own predicate:)

// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));

// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(), 
                       [](int i) { return i < 10; }), v.end());
Sign up to request clarification or add additional context in comments.

3 Comments

While elegant in the simple case, I'm dubious of its effectiveness in the real world. This wouldn't even work at all with a dynamic size vector.
"This wouldn't even work at all with a dynamic size vector." Why not?
While the std::remove_if lambda looks nice, but if I only want to delete exactly one element that matches the condition, it still goes through each element to the end. Therefore, I prefer manual iteration so I can break from the loop any time.
78

I agree with wilx's answer. Here is an implementation:

// curFiles is: vector < string > curFiles;

vector< string >::iterator it = curFiles.begin();

while(it != curFiles.end()) {

    if(aConditionIsMet) {

        it = curFiles.erase(it);
    }
    else ++it;
}

Comments

20

You can do that but you will have to reshuffle your while() a bit, I think. The erase() function returns an iterator to the element next after the erased one: iterator erase(iterator position);. Quoting from the standard from 23.1.1/7:

The iterator returned from a.erase(q) points to the element immediately following q prior to the element being erased. If no such element exists, a.end() is returned.

Though maybe you should be using the Erase-remove idiom instead.

2 Comments

Also, to downsize the vector(as it seems to what the OP wants to do) the swap trick can be used. Details here:stackoverflow.com/questions/253157/how-to-downsize-stdvector
It might be enough to say i=orbsList.erase(i) instead of orbsList.erase(i++)
11

erase returns a pointer to the next iterator value (same as Vassilis):

vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{   if(condition)
        mit = myVec.erase(mit);
    else
        mit++;
}

1 Comment

access the value at the iterator position using *mit
4

If someone need working on indexes

vector<int> vector;
for(int i=0;i<10;++i)vector.push_back(i);

int size = vector.size();
for (int i = 0; i < size; ++i)
{
    assert(i > -1 && i < (int)vector.size());
    if(vector[i] % 3 == 0)
    {
        printf("Removing %d, %d\n",vector[i],i);
        vector.erase(vector.begin() + i);
    }

    if (size != (int)vector.size())
    {
        --i;
        size = vector.size();
        printf("Go back %d\n",size);
    }
}

3 Comments

Why not just size-- i-- next to erase?
@user1122069 my good practices forbids such things, a lot of people do not understand difference between --i nad i--
The size variable is unnecessary altogether, compilers won't optimize when the vector is non-const and especially when you are mutating the vector in the loop body. So vector.size() will be called every iteration and you can just decrement i in the same scope you are erasing the item, and eliminate the second scope altogether. stackoverflow.com/a/55823447/6871623
1

As they said, vector's iterators get invalidated on vector::erase() no matter which form of iterator increment you use. Use an integer index instead.

3 Comments

Using the return value of erase you get a valid iterator back. So it's only necessary to assign the returned value to the iterator -- no performance issue here.
@harper: Actually, there's a big performance issue here; erasing an item from the middle of a vector requires all the rest of them to be moved down, which involves O(N) constructor and destructor calls each time.
And the use of an index has what befinit? How is the erasing performance increased? I just annotated the statement "iterators get invalidated .. no matter which form"...
1

You might want to consider using a std::list instead of a std::vector for your data structure. It is safer (less bug prone) to use when combining erasure with iteration.

Comments

0

Removing items from the middle of a vector will invalidate all iterators to that vector, so you cannot do this (update: without resorting to Wilx's suggestion).

Also, if you're worried about performance, erasing items from the middle of a vector is a bad idea anyway. Perhaps you want to use an std::list?

1 Comment

I know its a little late but you might wanna read this. Its contrary to what most people would expect but it comes from the creator of C++ bulldozer00.com/2012/02/09/vectors-and-lists

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.