2012-07-27 16 views
6

Załóżmy, że chcę usunąć przedmioty według niektórych kryteriów. Powiedzmy:Jak usunąć wartości z QMap?

QMap<int, int> map; 

i chcę usunąć wszystkie pozycje, w których wartość jest liczbą nieparzystą. Jeśli używam iterator:

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

Kod ten jest prawdopodobnie błędne, ponieważ wezwanie

map.remove(it.key()) 

unieważnia iteracyjnej. Jak mogę to zrobić bez resetu iteratora po każdym usunięciu?

+2

Pomoże mi to myśleć: http://stackoverflow.com/questions/263945/what-happens-if-you-all-erase-on-a-map-element-while-iterating- from-begin-to – Andrew

Odpowiedz

16

Zastosowanie QMap::erase zamiast, który zwraca iterator do elementu po jednej po prostu usunięte:

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

Innym sposobem jest użycie postfix przyrost operatora na iteracyjnej:

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

Jeszcze innym sposobem (i prawdopodobnie mniej wydajnym) jest użycie algorytmu STL remove_copy_if, a następnie 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); 

Nie mogę przetestować ostatniego przykładu w tej chwili.

+2

Powinieneś tylko inkrementować 'it', jeśli nie usunąłeś elementu; w przeciwnym razie przegapisz kolejny element. –

+0

Ups! Naprawiony. Dzięki. –

+0

Lub po prostu użyj [QMutableMapIterator] (http://doc.qt.io/qt-5/qmutablemapiterator.html#details). –

4

można byłoby lepiej wyłączyć za pomocą więcej STL-like erase funkcję:

  • Zajmuje iterator jako argument, a więc nie tracić czasu na poszukiwanie elementu poprzez jego klucza, kiedy już wiem gdzie to jest;
  • Powoduje przywrócenie iteratora do następnego elementu, aby można było kontynuować iterację.

Korzystanie z tym, można zaimplementować pętlę prawidłowo jako:

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

Myślę, że można uzyskać ten sam rezultat z map.remove((it++).key()), ale który byłby zarówno wolniejszy i Messiera niż erase.

Powiązane problemy