I have a program where I have to delete some entries of a vector of structs. Im doing it like this
for(int i=0; i<molec.size(); i++)
{
if(!molec[i].mine)
molec.erase(molec.begin()+i);
}
molec.mine is a boolean defined in the struct. The problem is that when I do this, it erases exactly half of the elements with molec.mine=false. I tried to search for some alternatives and found that I could do it with
vector.erase(std::remove(vec.begin(),vec.end(),1963),vec.end());
The thing with this is that I don't know how to do it with a vector of structs.
How can I solve this problem?
You are probably looking for std::remove_if. As in
molec.erase(
std::remove_if(
molec.begin(), molec.end(),
[](const auto& elem) { return !elem.mine; }),
molec.end());
i++ shouldn't be performed when element is erased. e.g.
for(int i=0; i<molec.size(); )
{
if(!molec[i].mine)
molec.erase(molec.begin()+i);
else
i++;
}
And yes, you should use algorithm library to avoid such issues.
molec.erase(std::remove_if(molec.begin(),molec.end(),[](const auto& v) { return !v.mine;}),molec.end());
Related
I am trying to make something like this:
v.erase(remove_if(v.begin(), v.end(), !pointer_to_func), v.end());
where v is a vector of strings, and pointer_to_func is my function-pointer that looks something like this:
bool (*pointer_to_func)(std::string);
And I know that the !pointer_to_func is wrong, however, that is the algorithm I am looking to produce somehow given the circumstances.
This is how it is declared in the class :
void Place::filter_ss(bool (*n)(std::string)){}; //function pointer labeled FP
I have searched all over, and I am not sure what else to do. Given that the function calls for filtering words in a vector to meet substring conditions, I figured remove_if() and copy() mash-up would do the trick however, I was wrong, and all options I have seen are deprecated/removed (i.e. not1, not2, ptr_fun, etc.).
Function is called as such in main program file:
int main(){
//places all push_back into vector v
Place p(){"Observatory", "Airport", "Delicatessen", "Theater", "Vacation"};
//this function is the pain, it take in the string directly and erases words not containing the substring
p.filter_ss(at);
//for loop should output: Observatory Delicatessen Theater Vacation
for (size_t x = 0; x < p.v.size(), x++)
cout << v[x] << " ";
... //Do something
... //Do something more
return 0;
}
My code only works with predefined test functions for example:
bool st(string str){
return str.find("st") < string::npos;
}
Any assistance/direction would be appreciated.
attempts aren't in order, nor are they alone in their own unique possibilities. These are general ideas of some of the things I have tried:
1.
void Place::filter_ss(bool (*n)(string)){
pointer_to_func = n;
vector<string> temp;
for(size_t t = 0; t < v.size(); t++){
if(pointer_to_func(v[t])){
temp.push_back(v[t]);
}
}
v.clear();
for(auto s : temp)
v.push_back(s);
}
2.
void Place::filter_ss(bool (*n)(string)){
pointer_to_func = n;
v.erase(remove_if(v.begin(), v.end(), not1(ptr_fun(pointer_to_func))), v.end());
}
3.
void Place::filter_ss(bool (*n)(string)){
vector<string> temp;
pointer_to_func = n;
copy_if(temp.begin(), temp.end(), back_inserter(v), pointer_to_func);
}
The various std::not functors were deprecated in C++17 and removed from C++20 because they added a better, generic alternative: std::not_fn:
v.erase(remove_if(v.begin(), v.end(),
std::not_fn(pointer_to_func), v.end());
I want to clear a element from a vector using the erase method. But the problem here is that the element is not guaranteed to occur only once in the vector. It may be present multiple times and I need to clear all of them. My code is something like this:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
std::vector<int>::iterator endIter = myNumbers_in.end();
for(; iter != endIter; ++iter)
{
if(*iter == number_in)
{
myNumbers_in.erase(iter);
}
}
}
int main(int argc, char* argv[])
{
std::vector<int> myNmbers;
for(int i = 0; i < 2; ++i)
{
myNmbers.push_back(i);
myNmbers.push_back(i);
}
erase(myNmbers, 1);
return 0;
}
This code obviously crashes because I am changing the end of the vector while iterating through it. What is the best way to achieve this? I.e. is there any way to do this without iterating through the vector multiple times or creating one more copy of the vector?
Use the remove/erase idiom:
std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());
What happens is that remove compacts the elements that differ from the value to be removed (number_in) in the beginning of the vector and returns the iterator to the first element after that range. Then erase removes these elements (whose value is unspecified).
Edit: While updating a dead link I discovered that starting in C++20 there are freestanding std::erase and std::erase_if functions that work on containers and simplify things considerably.
Calling erase will invalidate iterators, you could use:
void erase(std::vector<int>& myNumbers_in, int number_in)
{
std::vector<int>::iterator iter = myNumbers_in.begin();
while (iter != myNumbers_in.end())
{
if (*iter == number_in)
{
iter = myNumbers_in.erase(iter);
}
else
{
++iter;
}
}
}
Or you could use std::remove_if together with a functor and std::vector::erase:
struct Eraser
{
Eraser(int number_in) : number_in(number_in) {}
int number_in;
bool operator()(int i) const
{
return i == number_in;
}
};
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());
Instead of writing your own functor in this case you could use std::remove:
std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());
In C++11 you could use a lambda instead of a functor:
std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());
In C++17 std::experimental::erase and std::experimental::erase_if are also available, in C++20 these are (finally) renamed to std::erase and std::erase_if (note: in Visual Studio 2019 you'll need to change your C++ language version to the latest experimental version for support):
std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda
or:
std::vector<int> myNumbers;
std::erase(myNumbers, number_in);
You can iterate using the index access,
To avoid O(n^2) complexity
you can use two indices, i - current testing index, j - index to
store next item and at the end of the cycle new size of the vector.
code:
void erase(std::vector<int>& v, int num)
{
size_t j = 0;
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] != num) v[j++] = v[i];
}
// trim vector to new size
v.resize(j);
}
In such case you have no invalidating of iterators, complexity is O(n), and code is very concise and you don't need to write some helper classes, although in some case using helper classes can benefit in more flexible code.
This code does not use erase method, but solves your task.
Using pure stl you can do this in the following way (this is similar to the Motti's answer):
#include <algorithm>
void erase(std::vector<int>& v, int num) {
vector<int>::iterator it = remove(v.begin(), v.end(), num);
v.erase(it, v.end());
}
Depending on why you are doing this, using a std::set might be a better idea than std::vector.
It allows each element to occur only once. If you add it multiple times, there will only be one instance to erase anyway. This will make the erase operation trivial.
The erase operation will also have lower time complexity than on the vector, however, adding elements is slower on the set so it might not be much of an advantage.
This of course won't work if you are interested in how many times an element has been added to your vector or the order the elements were added.
There are std::erase and std::erase_if since C++20 which combines the remove-erase idiom.
std::vector<int> nums;
...
std::erase(nums, targetNumber);
or
std::vector<int> nums;
...
std::erase_if(nums, [](int x) { return x % 2 == 0; });
If you change your code as follows, you can do stable deletion.
void atest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end();) {
if (*it == number_in) {
it = container.erase(it);
} else {
++it;
}
}
}
However, a method such as the following can also be used.
void btest(vector<int>& container,int number_in){
container.erase(std::remove(container.begin(), container.end(), number_in),container.end());
}
If we must preserve our sequence’s order (say, if we’re keeping it sorted by some interesting property), then we can use one of the above. But if the sequence is just a bag of values whose order we don’t care about at all, then we might consider moving single elements from the end of the sequence to fill each new gap as it’s created:
void ctest(vector<int>& container,int number_in){
for (auto it = container.begin(); it != container.end(); ) {
if (*it == number_in) {
*it = std::move(container.back());
container.pop_back();
} else {
++it;
}
}
}
Below are their benchmark results:
CLang 15.0:
Gcc 12.2:
As the title says, I want to add an element to a std::vector in certain cases while iterating through the vector. With the following code, I'm getting an error "Debug assertion failed". Is it possible to achieve what I want to do?
This is the code I have tested:
#include <vector>
class MyClass
{
public:
MyClass(char t_name)
{
name = t_name;
}
~MyClass()
{
}
char name;
};
int main()
{
std::vector<MyClass> myVector;
myVector.push_back(MyClass('1'));
myVector.push_back(MyClass('2'));
myVector.push_back(MyClass('3'));
for each (MyClass t_class in myVector)
{
if (t_class.name == '2')
myVector.push_back(MyClass('4'));
}
return 0;
}
EDIT:
Well, I thought for each was standard C++, but it seems that it's a Visual Studio feature:
for each, in
Visual c++ "for each" portability
The act of adding or removing an item from a std::vector invalidates existing iterators. So you cannot use any kind of loop that relies on iterators, such as for each, in, range-based for, std::for_each(), etc. You will have to loop using indexes instead, eg:
int main()
{
std::vector<MyClass> myVector;
myVector.push_back('1');
myVector.push_back('2');
myVector.push_back('3');
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
if (myVector[i].name == '2')
{
myVector.push_back('4');
++size; // <-- remove this if you want to stop when you reach the new items
}
}
return 0;
}
As pointed out by pyon, inserting elements into a vector while iterating over it (via iterators) doesnt work, because iterators get invalidated by inserting elements. However, it seems like you only want to push elements at the back of the vector. This can be done without using iterators but you should be careful with the stop condition:
std::vector<MyClass> myVector;
size_t old_size = myVector.size();
for (int i=0;i<old_size;i++) {
if (myVector[i].name == '2') { myVector.push_back(MyClass('4')); }
}
After following the previous answers, you can use const auto& or auto& to have clean code. Should be optimized in release build by the compiler.
std::vector<MyClass> myVector;
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
const auto& element = myVector[i];
element.do_stuff();
}
#include<iostream>
#include<list>
using namespace std;
void compute(int num)
{
list<int> L;
list<int>::iterator i;
list<int>::iterator i2;
int p;
cout<<"Enter the number of numbers\n";
cin>>p;
int a;
for(int k=1;k<=p;k++)
{
cin>>a;
L.push_back(k);
}
cout<<endl;
for(i=L.begin() ; i!=L.end() ; ++i)
{
cout<<*i<<endl;
}
long int k=1;
for(i=L.begin() ; i!=L.end() ; ++i )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i2=L.erase(i);
}
k++;
}
for(i=L.begin() ; i!=L.end() ; ++i )
{
cout<<*i<<endl;
}
}
int main()
{
// int testcases, sailors;
//cin>>testcases;
//for(int i=1 ; i<=testcases ; i++)
{
// cin>>sailors;
}
//for(int i=1;i<=testcases;i++)
{
// int num;
//cin>>num;
//compute(num);
}
compute(0);
return 0;
}
I am trying to erase elements using L.erase() function in Lists. But I get an error saying
"Debug assertion failed! ......Expression:list iterator not incrementable"
but we CAN increment iterator right?
erase invalidates the iterator that was passed in as parameter - since the element at the position the iterator was pointing to was just erased! And on that same iterator, an increment is attempted in the next for loop in your code! That's why it fails.
However, erase it will return an iterator pointing to the new position, which we can use; a loop where you erase something from an STL container should therefore look something like the following; I show it with the type you use, list, but you could just as well use e.g. vector:
list<int> L;
// ...
list<int>::iterator it=L.begin();
while (it!=L.end())
{
if(eraseCondition)
{
it=L.erase(it);
}
else
{
++it;
}
}
Or, if possible, it's even better to use std::remove_if:
container.erase(std::remove_if(L.begin(), L.end(), predicate), L.end());
In your case that will be hard - if not impossible - to use since the predicate would need state information (the information whether the index is odd or even). So I'd recommend going with a loop structure as mentioned above; just keep in mind the remove_if for the general case of removing all elements where a certain predicate returns true!
Adding to what wOOte said, you may want to used a reverse iterator to get around the issue.
Technically not in this case.
When you use erase() you delete the node that was pointed to, so you actually invalidate the iterator you were on. So when you increment it it's undefined behavior.
It might be best to create a second list with just the iterators to the positions you'd like to delete, and you can cycle through those and call erase afterward. You wouldn't be erasing the iterators from the second list, so it'd work.
Something like this:
List<IteratorType> deleteList;
//Populate deleteList with every other element from original list.
for (List<IteratorType>::iterator iter = deleteList.begin();
iter !=deleteList.end; ++iter)
{
originalList.erase(*iter);
}
The iterator i is invalidated by the call to erase; however, in the next iteration of the for loop, you try to increment it - this is invalid.
Try
for(i=L.begin() ; i!=L.end() ; )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i=L.erase(i);
} else {
++i;
}
k++;
}
instead - only increment the iterator if you don't erase (erase basically "advances" the iterator because it yields an iterator to the element following the one you erased).
You can actually exploit this behaviour of erase to write your function without requiring k:
i = L.begin();
while ( i != L.end() ) {
i = L.erase( i ); // Delete one
if ( i != L.end() ) { // Skip next, if there's an element
++i;
}
}
So you delete the first element, skip the second, delete the third, and so on.
I am trying to create a data structure called Disjoint Set. Looking at theory what i thought of is something like this :
std::vector<std::pair<int,std::set<int>>> DisjointSet;
for(auto i=0;i<10;++i) DisjointSet.push_back( std::make_pair(i,std::set<int>().insert(i)));
but this code gives which is beyond my understanding , so this is good design to make Disjoint Set. Also how do i get rid of these errors?
set::insert doesn't return the set itself. You need to create the set beforehand, insert and then use it in make_pair. This does the trick with nearly no overhead for the copy using move:
std::vector< std::pair<int,std::set<int> > > DisjointSet;
for(auto i=0;i<10;++i) {
std::set<int> tmp; tmp.insert(i);
DisjointSet.push_back( std::make_pair(i,std::move(tmp)));
}
Well the following code does what I think you were attempting
std::vector< std::pair< int, std::set<int> > > DisjointSet;
for (int i=0; i<10; ++i)
{
std::set<int> tmp;
tmp.insert(i);
DisjointSet.push_back( std::make_pair(i,tmp) );
}
std::set<int>().insert(i)
does not return std::set.
Try to do it in several lines:
for(int i=0;i<10;++i) {
DisjointSet.push_back( std::make_pair(i,std::set<int>()) );
DisjointSet.back().insert(i);
}
You have a call to insert which makes no sense (since it doesn't return the set). So
std::vector<std::pair<int,std::set<int>>> DisjointSet;
for(auto i=0;i<10;++i)
DisjointSet.push_back( std::make_pair(i,std::set<int>().insert(i)));
Should really be something along the lines of
std::vector<std::pair<int,std::set<int>>> DisjointSet;
std::set<int> temp;
for(auto i=0;i<10;++i)
{
temp.insert(i)
DisjointSet.push_back( std::make_pair(i,temp) );
temp.clear();
}
A little more verbose, but its correct.