Removing item from vector while iterating?

C++StlIterator

C++ Problem Overview


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;
		}
	}

C++ Solutions


Solution 1 - C++

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());

Solution 2 - C++

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;
}

Solution 3 - C++

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.

Solution 4 - C++

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++;
}

Solution 5 - C++

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);
	}
}

Solution 6 - C++

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.

Solution 7 - C++

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.

Solution 8 - C++

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?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLucasView Question on Stackoverflow
Solution 1 - C++Moo-JuiceView Answer on Stackoverflow
Solution 2 - C++VassilisView Answer on Stackoverflow
Solution 3 - C++wilxView Answer on Stackoverflow
Solution 4 - C++PierreView Answer on Stackoverflow
Solution 5 - C++GelldurView Answer on Stackoverflow
Solution 6 - C++Maxim EgorushkinView Answer on Stackoverflow
Solution 7 - C++RaedwaldView Answer on Stackoverflow
Solution 8 - C++Oliver CharlesworthView Answer on Stackoverflow