11

Suppose I want to remove items according to some criterium. Let's say:

QMap<int, int> map;

and I want to remove all the items where value is an odd number. If I use an iterator:

for (auto it = map.begin(); it != map.end(); ++it)
    if (it.value() % 2 == 1)
        map.remove(it.key());

This code is probably wrong, since the call of

map.remove(it.key()) 

invalidates the iterator. How can I do this without reseting the iterator after each removal?

Martin Drozdik
  • 12,742
  • 22
  • 81
  • 146
  • 2
    This will help I think: http://stackoverflow.com/questions/263945/what-happens-if-you-call-erase-on-a-map-element-while-iterating-from-begin-to – Andrew Jul 27 '12 at 14:07

2 Answers2

29

Use QMap::erase instead, which returns an iterator to the element following the one you just erased:

for (auto it = map.begin(); it != map.end();)
    if (it.value() % 2 == 1)
        it = map.erase(it);
    else
        ++it;

Another way is to use the postfix increment operator on the iterator:

for (auto it = map.begin(); it != map.end();)
    if (it.value() % 2 == 1)
        map.erase(it++);
    else
        ++it;

Yet another way (and probably less efficient) is to use the STL remove_copy_if algorithm, followed by swap:

bool valueIsOdd(int value) {return value % 2 == 1;}

QMap<int,int> b;
std::remove_copy_if(a.begin(), a.end(),
                    std::inserter(b, b.end()),
                    &valueIsOdd);
a.swap(b);

I cannot test the last example at the moment.

Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
5

You would be better off using the more STL-like erase function:

  • It takes an iterator as an argument, and so doesn't waste time searching for the element by its key when you already know where it is;
  • It returns an iterator to the next element, so you can carry on iterating afterwards.

Using this, you can implement your loop correctly as:

for (auto it = map.begin(); it != map.end(); /* don't increment here */) {
    if (it.value() % 2 == 1) {
        it = map.erase(it);
    } else {
        ++it;
    }
}

I think you could get the same result from map.remove((it++).key()), but that would be both slower and messier than erase.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644