Удаление элементов из вектора в C++
Чтобы стереть элементы вектора по значению до С++ 20 необходимо:
#include #include using namespace std; int main() < vectordata< 99, 0, 578, 0, 258, 0, 84759>; cout cout cout
В C++20 есть функция std::erase(), которая позволяет стереть элемент вектора по значению.
#include #include using namespace std; int main() < system("chcp 1251>nul»); vector data< 5, 0, 10, 0, 12, 5, 74, 5 >; cout cout cout
Стереть элемент вектора по индексу
Стандартным решением для удаления элемента вектора является использование std::vector::erase(). Чтобы удалить элемент из вектора по его индексу, мы можем использовать арифметику указателя, как показано ниже:
#include #include #include #include using namespace std; template void remove(vector& v, size_t index) < v.erase(v.begin() + index); >int main() < vectordata = < 15, 28, 54, 32, 99 >; int index = 2; // 54 cout remove(data, index); cout << "\n\n\t\t"; copy(data.begin(), data.end(), ostream_iterator(cout, " ")); cout
В качестве альтернативы мы можем использовать std::advance() стандартный алгоритм для продвижения итератора на заданные позиции, чтобы указать на нужный индекс.
#include #include #include #include using namespace std; template void remove(vector& v, size_t index) < auto it = v.begin(); advance(it, index); v.erase(it); >int main() < vectordata = < 17, 2054, 34, 499, 51, 89, 387 >; int index = 4; //51 cout remove(data, index); cout << "\n\n\t\t"; copy(data.begin(), data.end(), ostream_iterator(std::cout, " ")); cout
Условное удаление элементов вектора
Стандартное решение для условного удаления элементов вектора:
#include #include #include #include using namespace std; int main() < vectordata= < 11, 32, 43, 44, 15, 77, 20 >; cout data.erase(remove_if(data.begin(), data.end(), [](const int& x) ), data.end()); cout << "\n\n\t\t"; copy(data.begin(), data.end(), ostream_iterator(cout, " ")); cout
Начиная с C++20, мы можем использовать std::erase_if() алгоритм, который стирает все элементы из вектора, удовлетворяющего предоставленному предикату. Он определен в заголовке
#include #include #include #include using namespace std; int main() < vectordata< "vova","vita","vasq","vova","luna","vita" >; cout cout ); copy(data.begin(), data.end(), ostream_iterator(cout, » «)); cout
Универсальный метод для условного стирания элементов вектора.
#include #include #include using namespace std; class num < int m_number = 0; public: void setnum(int num) < m_number = num; >int getnum() const < return m_number; >>; void if_less_erase(vector& mydata, int less) < vectortmpdata; for (int i = 0; i < mydata.size(); i++) < if (mydata[i].getnum() >less) tmpdata.emplace_back(mydata[i]); > mydata.clear(); for (int i = 0; i < tmpdata.size(); i++) < mydata.emplace_back(tmpdata[i]); >> int main() < random_device rd; mt19937 gen(rd()); uniform_int_distribution<>rside(100, 1000); vector data; for (int i=0;i <10;i++) < data.emplace_back(num()); data[i].setnum(rside(gen)); >cout if_less_erase(data, 500); cout cout
Как удалить элемент вектора по индексу?
Пытаюсь удалить элемент вектора если поле соответствует тому, что я ввел с клавиатуры, найденный мною способ на просторах интернета удаляет только два элемента, хотя должен по идее удалять все, код функции:
void Delete(vector &train) < string dest; cout > dest; for (int i = 0; i < train.size(); i++) if (train[i]->GetDest() == dest) train.erase(train.begin() + i); >
- Вопрос задан более двух лет назад
- 425 просмотров
Комментировать
Решения вопроса 1
Wataru @wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Не работает ваш код потому, что, допустим при i==3 вы удаляете элемент. Теперь элементом по индексу i будет следующий за удаленным элемент. Вы его не проверите и в конце итерации i увеличится на 1. Таким образом, следующие за удаляемым элементом вы просто пропустите.
Есть много способов исправить эту оплошность:
1) При удалении уменьшайте i на 1, чтобы следующее i++ от цикла for было отменено.
2) Вместо цикла for используйте while, где вы инкрементируете i только если элемент не удаляется.
3) Вместо if используйте цикл while, который удялял бы элемент в позиции i, пока его надо удалять (не забудьте проверить, что элемент, таки, существует — вы могли удалить последний элемент и i станет за границей массива).
4) (лучший вариант) Используйте remove. Мало того, что вам не надо изобретать велосипед, так этот метод еще и будет на порядок быстрее удаления по одному элементу. Потому что при каждом удалении у вас сдвигается часть массива и вы получаете квадратичное время работы на ровном месте.
Как удалить элемент из вектора c
О! у итератора перегружен оператор сложения? Прикольно. А я и не знал. Тогда всё действительно упрощается.
Добавлено 20.05.13, 06:55
Цитата applegame @ 20.05.13, 06:32
if(index != vec.size() — 1) vec[index] = vec.back();
vec.resize(vec.size() — 1);
resize() уменьшает размер. Это понятно. Но такой случай подходит, только если нужно удалить последний элемент.
А вот зачем нужно перемещать последний элемент, не понимаю.
Возможно, мне стоило упомянуть, что порядок элементов , очень даже важен.
Боюсь, что вашь вариант интересен, но не подходит.
Сообщ. #5 , 20.05.13, 07:33

Рейтинг (т): 6
О! у итератора перегружен оператор сложения? Прикольно
В любом случае есть еще std::advance
Сообщ. #6 , 20.05.13, 07:57

Цитата popsa @ 20.05.13, 07:33
В любом случае есть еще std::advance
И то верно. Когда-то пролистывал описание заголовочного файла iterator, но не обратил внимание.
advance мне нравится больше.
А за одно попалась ещё и distance, которая мне тоже пригодится.
Сообщ. #7 , 20.05.13, 08:33
Senior Member
Рейтинг (т): 24
Цитата Eric-S @ 20.05.13, 06:50
resize() уменьшает размер. Это понятно. Но такой случай подходит, только если нужно удалить последний элемент.
А вот зачем нужно перемещать последний элемент, не понимаю.
Этот способ подходит для удаления любого элемента. Последний ставится на место удаляемого. После чего ресайзом удаляется последний элемент.
Цитата Eric-S @ 20.05.13, 06:50
Возможно, мне стоило упомянуть, что порядок элементов , очень даже важен.
А вот порядок да. Этот вариант годится только если порядок неважен.
Сообщение отредактировано: applegame — 20.05.13, 12:12
Сообщ. #8 , 20.05.13, 08:47

applegame, ваш вариант, частный случай. Меня же интересовал именно общий. Но всё равно спасибо, возможно пригодится в будущем.
Тут появился новый вопрос:
А возможно ли скопировать вектор, пропустив один элемент с указанным индексом?
Я тут дополнительно задумался над безопастностью, точнее возможностью отката изменений, в случае исключения.
Дело в том, что там выполняются разные операции и как-то надо откатить удаление, если последующие операции глюканут.
Есть красивое решение, создать копию текущего объекта, внего скопировать все данные, по ходу внося изменения. А потом, заменить данные из текущего объекта, на данные из временного.
someclass tmp;
// как можно вот это заоптимизировать?
tmp.vec = vec;
tmp.vec.erase( std::advance( tmp.vec.begin(), index ) );
std::swap( vec, tmp.vec );
Сообщ. #9 , 20.05.13, 09:04

Рейтинг (т): 6
tmp.vec.erase( std::advance( tmp.vec.begin(), index ) );
advance же void возвращает скорее надо
VecClass::iterator it = tmp.vec.begin();
std::advance(it, index);
tmp.vec.erase(it);
Я тут дополнительно задумался над безопастностью, точнее возможностью отката изменений, в случае исключения.
Дело в том, что там выполняются разные операции и как-то надо откатить удаление, если последующие операции глюканут.
А какие еще операции выполняются помимо удаления?
Сообщ. #10 , 20.05.13, 09:58

Цитата popsa @ 20.05.13, 09:04
А какие еще операции выполняются помимо удаления?
В моём конкретном случае.
В памяти висит таблица рядов. Класс
class memory_table
Название столбцов, вынесены в отдельный вектор
std::vector _names;
Идентификаторы типов столбцов, вынесены в другой вектор:
std::vector _types;
Каждый ряд, представляет из себя вектор метоморфных полей:
typedef std::vector < object_ptr >numeric_row_type;
ряды хранятся в списке:
std::list < numeric_row_type >_rows;
Нужно реализовать метод удаления столбца. Соответственно, требуется удалить элемент из _names, _types и каждого row.
void memory_table::remove_column( std::size_t index )
// проверить индекс
if( count_column() <= index )
throw std::invalid_argument( "colum is not exists." );
// заблокировать таблицу от чтения и модификации
enter_write_guard lk( this );
// удалить название
auto it_name = _names.begin();
std::advance( it_name, index );
_names.erase( it );
// удалить тип
auto it_type = _types.begin();
std::advance( it_type, index );
_type.erase( it );
// перебрать все ряды
for( auto it = _rows.begin(); it != _rows.end(); it++ )
// ссылка на ряд
numeric_row &values = *it;
// если есть такое значение
if( values.size() > index )
// удалить значение
auto it_value = values.begin();
std::advance( it_value, index );
values.erase( it );
Вот, как-то примерно так. Слегка почищенный вариант. Ещё не компилил.
Сообщение отредактировано: Eric-S — 20.05.13, 10:02
Сообщ. #11 , 20.05.13, 12:13

Рейтинг (т): 527
Цитата Eric-S @ 20.05.13, 06:50
О! у итератора перегружен оператор сложения? Прикольно. А я и не знал.
Не у всех, только у random access intertors.
Цитата Eric-S @ 20.05.13, 07:57
advance мне нравится больше.
А за одно попалась ещё и distance, которая мне тоже пригодится.
Нет смысла. По Стандарту std::vector<> обязан предоставлять итераторы с произвольным доступом. Функции std::advance() и std::distance() предназначены для обобщённых алгоритмов, когда ты не знаешь типа итераторов.
Сообщ. #12 , 20.05.13, 12:15
Senior Member
Рейтинг (т): 24
Цитата Eric-S @ 20.05.13, 08:47
applegame, ваш вариант, частный случай. Меня же интересовал именно общий.
Извините, не понимаю в чем его частность. Он работает для любых массивов и любых элементов. Вполне себе общий вариант. Наверное вы хотели сказать: «Меня интересовал вариант с сохранением порядка», тогда да, вопросов больше не имею.
Сообщение отредактировано: applegame — 20.05.13, 12:16
Сообщ. #13 , 20.05.13, 12:26

Рейтинг (т): 527
Цитата Eric-S @ 20.05.13, 08:47
Я тут дополнительно задумался над безопастностью, точнее возможностью отката изменений, в случае исключения.
Дело в том, что там выполняются разные операции и как-то надо откатить удаление, если последующие операции глюканут.
Это называется строгой гарантией отказоустойчивости: функция (метод, оператор) либо полностью исполняют свой контракт, либо не вносят никаких изменений в операционного окружение. Для всех Стандартных контейнеров для каждого метода в Стандарте указывается предоставляемый каждым из них уровень гарантии. В частности std::vector<>::erase(std::vector<>::end()-1) обеспечивает строгую гарантию, однако иные аргументы дают только базовую: метод либо полностью исполняет свой контракт, либо допускает некоторые изменения в операционном окружении, каковые гарантировано не имеют утечек ресурсов и потери данных пользователя, а все структуры данных остаются инвариантными.
Цитата Eric-S @ 20.05.13, 08:47
Есть красивое решение, создать копию текущего объекта, внего скопировать все данные, по ходу внося изменения. А потом, заменить данные из текущего объекта, на данные из временного.
Всегда следует стремиться к обеспечению строгой гарантии в своём коде. Базовая гарантия может быть выбрана только в случае, если обеспечение строгой обходится дорого. К примеру рассматриваемый метод, реализуемый в соответствии с требованиями базовой гарантии, оказывается гораздо более эффективным, что для библиотечного кода важнее. Пользователь std::vector<> знает об этом и либо просто пользуется, если это его устраивает, либо пишет строго отказоустойчивый код самостоятельно, когда это ему критично.
Как удалить элементы вектора без сдвига?
Допустим у меня есть каталог продуктов и я хочу удалить какой-то элемент по ID (введенному с клавиатуры), который равен индексу этого элемента. При первом удаление все в порядке, но при вторичном – вектор сдвигается и удаляется не тот элемент. Элементами вектора являются экземпляры класса.
void Car::buyCar(int buyId)
system("cls"); std::cout > client.name; clientBase.push_back(client); std::cout > cars.id; system("cls"); std::cout
- Вопрос задан более двух лет назад
- 373 просмотра
Комментировать
Решения вопроса 1
Вариант 1: не удаляйте элемент, помечайте проданным (или введите соглашение, что допустим, машины с - проданы)
Вариант 2: используйте std::map
Ответ написан более двух лет назад
Нравится 1 20 комментариев
Можно и вектор оставить, просто удалять не по индексу, а сначала искать в списке машину с нужным id (не супер производительное решение, разумеется, впрочем, vector::erase() тоже имеет линейную сложность)
olkhovich @olkhovich Автор вопроса
Вариант 1: не удаляйте элемент, помечайте проданным (или введите соглашение, что допустим, машины с - проданы)
Тоже думал об этом, но мне кажется, что не самый красивый вариант получается.
Вариант 2: используйте std::map
Наверное всё-таки нет, тк id это свойство класса Сar . Да и переписывать довольно много придется.
olkhovich @olkhovich Автор вопроса
galaxy, добавить еще один проход по каталогу или я не так понял?
Наверное всё-таки нет, тк id это свойство класса
Вас же несмущает, что в подходе с vector вы считаете его индексом в векторе. Думаю, map - самый нормальный вариант.
добавить еще один проход по каталогу или я не так понял?
Сначала найти нужный Car в векторе, потом удалить. За синтаксис не ручаюсь:
auto car = std::find_if(std::begin(catalog), std::end(catalog), [buyId](Car c)); catalog.erase(car);
olkhovich @olkhovich Автор вопроса
galaxy, хорошо, попробую несколько вариантов, спасибо.
olkhovich @olkhovich Автор вопроса
galaxy, вариант рабочий, большое спасибо ещё раз, но теперь возникла проблема с добавлением этого элемента в другой вектор, весь день не получается, не подскажите как решить? Ещё один проход и поиск занимается много времени. А push_back невозможен из-за несоответствии типов.
Код покажите
olkhovich @olkhovich Автор вопроса
system("cls"); std::cout > client.name; std::cout clientBase; std::vector garage;)) cars.printCatalog(); std::cout > cars.id; system("cls"); std::cout
При добавлении по Id происходит такой же сдвиг, больше идей уже нет.
olkhovich @olkhovich Автор вопроса
galaxy, При обычном client.garage.push_back(cars); естественно добавляется элемент только с нужным id, остальные свойства нулевые.
А можно как-то полный код, а то я уже не понимаю ничего?
client.clientBase.push_back(client)
вот разве clientBase - свойство клиента?
И словами можете описать кратко, что должно происходить?
olkhovich @olkhovich Автор вопроса
вот разве clientBase - свойство клиента?
Вообще да, хоть это не очень хороший вариант
А можно как-то полный код, а то я уже не понимаю ничего?
case 5: system("cls"); std::cout > client.name; std::cout clientBase;)) cars.printCatalog(); std::cout > cars.id; system("cls"); std::cout
class Car < public: int id; double price; std::string color; double speed; std::vectorcatalog; std::vector carColors< "white", "black", "red", "blue", "green", "yellow", "purple", "gray" >; Car(); Car(int id, double price, std::string color, double speed); void setCatalog(); void printCatalog(double filterPrice = 6.50, double filterSpeed = 0); void buyCar(int buyId); >; void Car::buyCar(int buyId) < auto car = std::find_if(std::begin(catalog), std::end(catalog), [buyId](Car c) < return c.id == buyId; >); catalog.erase(car); >
class Client: public Car < public: std::string name; std::vectorgarage; std::vector clientBase; Client(); Client(std::string name, std::vector garage); >;
И словами можете описать кратко, что должно происходить?
При выборе и покупке автомобиля происходит удаление этого автомобиля из каталога( cars.buyCar(cars.id); ))
И должно происходить добавление этого же автомобиля в гараж клиента, то есть просто перемещение из вектора каталога в вектор гараж.
Ужас какой-то с свойствами:
- clientBase - получается, у каждого клиента он свой, и содержит единственную запись - его самого? странно
- cars - какой смысл это переменной у вас? Она что содержит?
- Car::catalog - опять получается, у каждой машины свой каталог, и они все разные, вообще говоря
client.garage.push_back(cars);
- это не добавление купленного автомобиля (я писал выше, не понимаю, что в переменной cars)
верните car из Car::buyCar и ее добавляйте:
auro carBought = cars.buyCar(cars.id) client.garage.push_back(carBought);
olkhovich @olkhovich Автор вопроса
Ужас какой-то с свойствами:
Да, бред получается, перенес все. Немного запутался.
верните car из Car::buyCar и ее добавляйте:
auro carBought = cars.buyCar(cars.id)
client.garage.push_back(carBought);
Я правильно понял?
auto carBought = cars.buyCar(cars.id); client.garage.push_back(carBought); catalog.erase(carBought);
auto Car::buyCar(int buyId) < auto car = std::find_if(std::begin(catalog), std::end(catalog), [buyId](Car c) < return c.id == buyId; >); return car; >