Próbowałem usunąć szereg elementów z mapy w oparciu o określone warunki. Jak to zrobić za pomocą algorytmów STL?
Początkowo myślałem o użyciu, remove_if
ale nie jest to możliwe, ponieważ remove_if nie działa dla kontenera asocjacyjnego.
Czy istnieje równoważny algorytm „remove_if”, który działa na mapie?
Jako prostą opcję pomyślałem o przejściu mapy i wymazaniu. Ale czy zapętlanie mapy i kasowanie bezpiecznej opcji? (Ponieważ iteratory stają się nieważne po wymazaniu)
Posłużyłem się następującym przykładem:
bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}
int main(void)
{
std::map<int, std::string> aMap;
aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";
// does not work, an error
// std::remove_if(aMap.begin(), aMap.end(), predicate);
std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
return 0;
}
for(auto iter=aMap.begin(); iter!=aMap.end(); ){ ....}
aby zmniejszyć bałagan. Reszta jest taka, jak mówili inni. To pytanie uratowało mi trochę rozdwajania włosów ;-)Odpowiedzi:
Prawie.
To, co miałeś pierwotnie, zwiększyłoby iterator dwukrotnie , gdybyś wymazał z niego element; możesz potencjalnie pominąć elementy, które należało usunąć.
Jest to powszechny algorytm, z którego korzystałem i udokumentowałem w wielu miejscach.
[EDYCJA] Masz rację, że iteratory są unieważniane po wymazaniu, ale tylko iteratory odwołujące się do elementu, który jest usuwany, inne iteratory są nadal ważne. Stąd użycie
iter++
werase()
rozmowie.źródło
map
, zwracają następny iterator zerase(iter)
. Jest to o wiele czystszeiter = erase( iter )
.erase_if dla std :: map (i innych kontenerów)
Używam do tego następującego szablonu.
To nic nie zwróci, ale usunie elementy ze std :: map.
Przykład użycia:
Drugi przykład (pozwala przekazać wartość testową):
źródło
std
. Rozumiem, dlaczego nie jest członkiemstd::map
, ale myślę, że coś takiego powinno znajdować się w standardowej bibliotece.std::map
i innych.Teraz
std::experimental::erase_if
jest dostępny w nagłówku<experimental/map>
.Zobacz: http://en.cppreference.com/w/cpp/experimental/map/erase_if
źródło
Mam tę dokumentację z doskonałej referencji SGI STL :
Tak więc iterator, który masz, który wskazuje na element do usunięcia, zostanie oczywiście unieważniony. Zrób coś takiego:
źródło
erase
wywołaniem. Więc rzeczywiście są równoważne. Mimo to zdecydowanie wolałbym twoją wersję od oryginału.Oryginalny kod ma tylko jeden problem:
Tutaj wartość
iter
jest zwiększana raz w pętli for i raz w kasowaniu, co prawdopodobnie zakończy się w jakiejś nieskończonej pętli.źródło
Oto eleganckie rozwiązanie.
źródło
Z dolnych nut:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
Kontener asocjacyjny pary nie może zapewnić mutowalnych iteratorów (zgodnie z definicją w wymaganiach Trivial Iterator), ponieważ typ wartości zmiennego iteratora musi być Assignable, a para nie jest Assignable. Jednak kontener asocjacyjny pary może zapewnić iteratory, które nie są całkowicie stałe: iteratory takie, że wyrażenie (* i). Sekunda = d jest prawidłowe.
źródło
Pierwszy
Po drugie, poniższy kod jest dobry
Podczas wywoływania funkcji parametry są oceniane przed wywołaniem tej funkcji.
Więc kiedy iter ++ jest oceniane przed wywołaniem wymazywania, operator ++ iteratora zwróci bieżący element i wskaże następny element po wywołaniu.
źródło
IMHO nie ma
remove_if()
odpowiednika.Nie możesz zmienić kolejności mapy.
Nie
remove_if()
możesz więc umieścić swoich interesujących par na końcu, na które możesz sprawdzićerase()
.źródło
Na podstawie odpowiedzi Iron Saviour Dla tych, którzy chcieliby zapewnić zakres bardziej podobny do standardowych iteratorów funkcjonalnych.
Ciekawe, czy istnieje sposób na zgubienie
ContainerT
przedmiotów i uzyskanie tego z iteratora.źródło
Odpowiedź Steve Folly wydaje mi się bardziej efektywna.
Oto kolejne łatwe, ale mniej wydajne rozwiązanie :
Rozwiązanie wykorzystuje
remove_copy_if
do skopiowania żądanych wartości do nowego kontenera, a następnie zamienia zawartość oryginalnego kontenera na zawartość nowego:źródło
Jeśli chcesz usunąć wszystkie elementy z kluczem większym niż 2, najlepszym sposobem jest
Działa jednak tylko dla zakresów, a nie dla żadnego predykatu.
źródło
Używam w ten sposób
źródło