Я пытался стереть ряд элементов с карты в зависимости от определенных условий. Как это сделать с помощью алгоритмов STL?
Сначала я думал об использовании, remove_if
но это невозможно, поскольку remove_if не работает для ассоциативного контейнера.
Есть ли какой-либо эквивалентный алгоритм remove_if, который работает для карты?
В качестве простого варианта я подумал о циклическом просмотре карты и стирании. Но есть ли безопасный вариант перебора карты и удаления? (Поскольку итераторы становятся недействительными после стирания)
Я использовал следующий пример:
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(); ){ ....}
чтобы уменьшить беспорядок. Остальное, как говорили другие. Этот вопрос только что избавил меня от секущихся волос ;-)Ответы:
Почти.
То, что у вас было изначально, увеличило бы итератор вдвое, если бы вы удалили из него элемент; потенциально вы можете пропустить элементы, которые нужно стереть.
Это распространенный алгоритм, который я видел и документировал во многих местах.
[EDIT] Вы правы, что итераторы становятся недействительными после стирания, но только итераторы, ссылающиеся на удаляемый элемент, другие итераторы остаются действительными. Следовательно, использование
iter++
вerase()
вызове.источник
map
, возвращают следующий итератор изerase(iter)
. Это намного чищеiter = erase( iter )
.erase_if для std :: map (и других контейнеров)
Для этого я использую следующий шаблон.
Это ничего не вернет, но удалит элементы из std :: map.
Пример использования:
Второй пример (позволяет передать тестовое значение):
источник
std
. Я понимаю, почему он не является членомstd::map
, но я думаю, что что-то подобное должно быть в стандартной библиотеке.std::map
и других.Теперь
std::experimental::erase_if
доступно в заголовке<experimental/map>
.См .: http://en.cppreference.com/w/cpp/experimental/map/erase_if
источник
Я получил эту документацию из отличного справочника SGI STL :
Итак, имеющийся у вас итератор, который указывает на удаляемый элемент, конечно же, будет недействителен. Сделайте что-нибудь вроде этого:
источник
erase
. Так что они действительно эквивалентны. Тем не менее, я бы предпочел вашу версию оригиналу.В исходном коде есть только одна проблема:
Здесь
iter
увеличивается один раз в цикле for и еще раз в стирании, что, вероятно, закончится каким-то бесконечным циклом.источник
Вот какое изящное решение.
источник
Из нижних нот:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
ассоциативный контейнер пары не может предоставлять изменяемые итераторы (как определено в требованиях к тривиальному итератору), потому что тип значения изменяемого итератора должен быть назначаемым, а пара - не назначаемым. Однако парный ассоциативный контейнер может предоставлять итераторы, которые не являются полностью постоянными: итераторы, так что выражение (* i) .second = d является допустимым.
источник
Первый
Во-вторых, следующий код хорош
При вызове функции параметры оцениваются перед вызовом этой функции.
Таким образом, когда iter ++ оценивается перед вызовом удаления, оператор ++ итератора вернет текущий элемент и укажет на следующий элемент после вызова.
источник
ИМХО
remove_if()
эквивалента нет .Вы не можете изменить порядок карты.
Так что
remove_if()
нельзя ставить интересующие пары в конец, на который можно уравнятьerase()
.источник
На основе ответа Iron Savior Для тех, кто хотел бы предоставить более широкий спектр функциональных итераторов std.
Любопытно, есть ли способ потерять
ContainerT
элементы и получить их от итератора.источник
Я считаю, что ответ Стива Фолли более эффективен.
Вот еще одно простое, но менее эффективное решение :
Решение использует
remove_copy_if
для копирования нужных нам значений в новый контейнер, а затем меняет местами содержимое исходного контейнера с содержимым нового:источник
Если вы хотите стереть все элементы с ключом больше 2, то лучший способ -
Однако работает только для диапазонов, а не для любого предиката.
источник
Я использую вот так
источник