Search code examples
c++vectoreraseerase-remove-idiom

vector erase specific indexes with iterators (not based on range or condition)


Say, I have two vector as follows in the code, I want to erase the elements indexed by vector "index_to_filter" in the vector "data" using iterators. The dummy way in the code is just to point the obvious error. So far, I couldn't get it working, nor, figured out if this could be an erase-remove-idiom?. Is there a way to and am missing it ?

Thx.

#include <iostream>
#include <vector>

int main()
{

std::vector<int> data{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<int> index_to_filter{ 1, 5, 8 };

/* needed result data = { 0, 2, 3, 4, 6, 7, 9 }*/

std::vector<int>::iterator iter = index_to_filter.begin();
while (iter != index_to_filter.end())
{
    std::vector<int>::iterator iter_data = data.begin() + *iter;
    iter_data = data.erase(iter_data);
    iter++;
}
/* Throws : vector erase iterator outside range */

for (int i: data)
    std::cout << i << std::endl;

system("pause");
return 0;
}

PS: the vector.erase question is aborded tens of times here but found no clue for this one !

PS: Solutions without iterators are not welcome. (No offense !)

thanks


Solution

  • Your problem is very simple:

    std::vector<int> index_to_filter{ 1, 5, 8 };
    

    You intent is to remove elements #1, #5, and #8 from the other array, and you start with element #1:

    Value    0 1 2 3 4 5 6 7 8 9
    Index    0 1 2 3 4 5 6 7 8 9
               ^       ^     ^
    

    The bottom line, the "index" line, is the index into the vector. The top line, the "value" line, is the value in that position in the vector. When you start, the two values are the same.

    The carets mark the indexes you wish to remove, and you start with element #1.

    The fundamental gap that you are ignoring is that when you remove an element from the vector, you do not exactly have a gaping black hole, a void in that position. All subsequent values in the container shift over. So, when you remove element #1, the remaining values shift over:

    Value    0 2 3 4 5 6 7 8 9
    Index    0 1 2 3 4 5 6 7 8
                       ^     ^
    

    The next element you wish to remove is element #5. Unfortunately, the value at that position in the vector is no longer 5. It is 6, because the array has shifted. Your code than proceeds and removes index position #5, which has the following result:

    Value    0 2 3 4 5 7 8 9
    Index    0 1 2 3 4 5 6 7
                             ^
    

    You have already gone off the rails here. But now, your code attempts to remove index #8, which no longer exists, since the vector is now shorter. As soon as your code attempts to do that, you blow up.

    So, in conclusion: what you're missing is the simple fact that removing a value from a middle of a vector shifts all subsequent values up by one position, in order to fill the gap left from the removed element, and the code you wrote fails to account for that.

    The simplest solution is to remove elements from the highest index position to the lowest. In your code, you already have index_to_filter in sorted order, so instead of iterating from the beginning of index_to_filter to its end, from the lowest to the highest index, iterate backwards, from the last index in index_to_filter to the first, so your code attempts to remove indexes 8, 5, then 1, so that each time the removal of the element does not affect the lower index positions.