Iterate a STL set till the second last index - c++

I want to check the difference of two consecutive elements of set so I want to iterate it till the second last element
This is the code written by me
for(auto i=s.begin();i!=s.end()-1;i++)
{
if(s[*i+1]-s[*i]!=1)
{
cout<<s[*i]+1<<endl;
check=true;
break;
}
}
but this code is giving me errors answer subtraction is not allowed in iterators or no '-' is recognized so can anybody help me with that

Another way to do this is to have two iterator stepping through the set, one of them one position behind the other.
CppReference has an example implementation of adjacent_find which does this.
Note that however you do it, your code needs to check that the set has more than one element in it. Both your code and #cigiens code will do the wrong thing when given a set with only one element.

+ and - only work on random access iterators. Instead you can use std::prev and std::next to get the previous and next iterators into a std::set.
Also, your usage of the iterators is incorrect, e.g. s[*i] is not the value of the element pointed at by i. Instead, you have to just write *i.
So your code can be written like this:
for(auto i = s.begin(), second_last = std::prev(s.end()); i != second_last; ++i)
{
if(*std::next(i) - *i != 1)
{
std::cout << *i + 1 << std::endl;
// ...
}
}
Also, this loop will invoke undefined behavior if the set is empty, since the value of second_last will be an invalid iterator. So you can do a check for that as well:
if (not s.empty())
{
// ... the above loop
}

Related

How to tell if the directory_entry is the end of directory_iterator in C++

using std::filesystem, how can I tell that the directory_entry is the end of a directory_iterator?
here's an example of what I'm trying to do:
auto it = fs::directory_iterator(path);
for (const auto &i : it) {
if (i == it.back()) { // this works with `vector`s but not with `directory_iterator`
doSomethingWith(i); // call this function only on the last entry.
}
}
From docs -
If the directory_iterator reports an error or is advanced past the last directory entry, it becomes equal to the default-constructed iterator, also known as the end iterator. Two end iterators are always equal, dereferencing or incrementing the end iterator is undefined behavior.
However, the issue is incrementing directory_iterator invalidates all previous values.
So, one possible solution is to use the post-increment operation to find the last directory_entry.
auto it = fs::directory_iterator(path);
for (auto i = fs::begin(it); fs::begin(i) != fs::end(it); ) {
auto entry = *(i++);
if (fs::begin(i) == fs::end(it)) { // sugercoat of `i == fs::directory_iterator{}`
doSomethingWith(entry); // this is the last directory entry
} else {
doSomethingElse(entry); // non-last entry
}
}
You can use std::filesystem::end. See the docs here:
https://en.cppreference.com/w/cpp/filesystem/directory_iterator/begin
But in your example, you shouldn’t need to explicitly check as the loop will end correctly on its own.
A default constructed directory_iterator is the end iterator. See entry one in the constructor list: https://en.cppreference.com/w/cpp/filesystem/directory_iterator/directory_iterator

how to erase an element hasn't a certain character in vector elements

i found many answers for erase an element of vector has a certain character
but i tried to make some of these solutions to erase the element which hasn't that character but dosen't work
for(int k=0; k<temp.size();k++)
{
while(temp[k].find_first_of("xX")!= string::npos)
{
temp.erase(temp.begin()+k);
}
}
variables_print.push_back(temp);
here an example , these code erase the elements have char "xX" but i tried to make it temp[K].find_first_not_of("xX") and doesn't work
also make it temp[K].find_first_of("xX")== string::npos and doesn't work
how to erase the elements haven't x or X characters
You could do it this way:
auto newEnd = std::remove_if(v.begin(), v.end(),
[](const auto& s) { return s.find_first_of("xX") == std::string::npos; });
v.erase(newEnd, v.end());
remove_if moves all elements not matching the condition to front, replacing those that satisfy the it. The condition here is satisfied when the lambda given as the third argument returns true. newEnd is the iterator pointing to the first element after those that are not removed.
For example, if input is this: {"aaa", "bbx", "ccc"}, after call to remove_if the vector looks like this: {"aaa", "ccc", <used to be bbx>}.
The second line removes all elements starting fromnewEnd. So in example above, you end up with {"aaa", "ccc"}.
The condition here is a lambda which returns true for each element that contains neither 'x' nor 'X'.
This is the same condition you tried - and is correct one. Problem with your original code different.
Look at: while(temp[k].find_first_of("xX")!= string::npos). If the string does not contain X, the body of this nested loop will not be executed and nothing gets removed. Also, you could replace the loop with a simple if statement.
There's another problem with the outer loop. You increment k each time, even if you've just removed an element. Consider this example: {"x", "x"}. When incrementing k each time, you will skip the second string and end up with {"x"}.
The corrected code looks would look like this:
for(size_t k=0; k<v.size(); )
{
if(v[k].find_first_of("xX") == std::string::npos)
{
v.erase(v.begin()+k);
}
else
{
++k;
}
}
Compare this with the first version. It's not only shorter, but also leaves much less room for bugs.
As #Bob_ points out in comments, the first version is known as erase-remove idiom. It's a common thing to see in modern C++ (i. e. C++11 and newer), so it's worth getting used to it and using it.

Delete elements in vector by position?

In my class I am trying to remove an element from a std::vector using a for loop. However, when I try to remove the element, I receive an error and I am not quite sure how to solve it. The error I get is:
Error 4 error C2679: binary '+' : no operator found which takes a right-hand operand of type 'Enemy *' (or there is no acceptable conversion)
void Enemy::UpdateEnemies(SDL_Renderer * render)
{
for (int i = enemies.size() - 1; i >= 0; i--)
{
enemies[i]->Update();
if (enemies[i]->Active == false)
{
// Receive the error here
enemies.erase(enemies.begin() + enemies.at(i));
}
}
if ((SDL_GetTicks()-prevSpawnTime)/1000.0f > enemySpawnTime)
{
prevSpawnTime = SDL_GetTicks();
//Add an anemy
AddEnemy(render);
}
}
Other answers have given you the naive solution. However, if you have more than one enemy to remove, you need a better solution.
Using std::remove_if from <algorithm> would be better in this case. That will avoid repeated shuffling of the items in your vector. It works by moving all the ones you want to remove to the end of the container, and then giving you an iterator to the beginning of those.
auto removed_iter = std::remove_if( enemies.begin(), enemies.end(),
[]( const Enemy * e ) { return e->IsActive(); } );
enemies.erase( removed_iter, enemies.end() );
In this case you would have to update all your enemies first. If that doesn't need to be done in reverse order, then:
for( auto e : enemies ) e->Update();
Assuming that you want to remove the i-th element, you need to do
enemies.erase(enemies.begin() + i);
or better
enemies.erase(std::next(enemies.begin(), i));
In your case enemies.at(i) returns the dereferenced iterator at position i, i.e. an element of type Enemy, and not i or the iterator for position i.
A better way is to use reverse iterators:
for(auto it = enemies.rbegin(); it != enemies.rend(); ++it)
{
(*it)->Update();
if ((*it)->Active == false)
{ // need `base()` to convert to regular iterator, then substract 1
enemies.erase(std::prev(it.base())); // remove the current position
/* or, equivalently
enemies.erase(std::next(it).base());
*/
}
}
This line:
enemies.erase(enemies.begin() + enemies.at(i));
enemise.at(i) returns the Enemy that is stored in the vector.
enemies.begin() is a pointer
As the error says, you are trying to add pointer and vector.
You probably want just to call:
enemies.erase(enemies.begin() + i);
In addition to what others have said, I would take it a step further and suggest that erasing from a vector within a loop could cause undefined behavior. I don't see a break, so I assume that there could be multiple inactive enemies. Instead of writing your own loop, consider using the std::remove_if algorithm. Essentially your code is an attempt to add an iterator with an object reference which will fail to compile. The remove_if solution will essentially copy all enemies where Active==false to the end of the container while shifting everything else forward. It provides a convenient way to first identify the things to remove, and then erase them all at once. Additionally if you don't have a C++11 compiler the same thing will work if you use a different kind of predicate. The remove_if link contains an example of a function, but you can also use a functor.
enemies.erase(std::remove_if(enemies.begin(), enemies.end(), [](const Enemy* e){ return e->Active == false; }), enemies.end());
For more information check these out.
What is a lambda expression in C++11?
http://www.cplusplus.com/reference/algorithm/remove_if/
C++ Functors - and their uses

Accessing an element and its successor in a list at the same time

I have a list and when iterating over it i want to access the element at the iterator and the following element at the iterator+1 at the same time. Here is my attempt:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator iterator = teamlist.begin(); iterator != teamlist.end(); std::advance(iterator,2))
{
match(*(*iterator), *(*(++iterator)));
}
The match function does nothing to the iterators, it just get's some values of the teams out of it to calculate a match result.
But the ++iterator does not work, as the elements in the matchfunction are still the same. How do I do this?
Passing the iterator and operating on the iterator in the same pass (like increment of the iterator) leads to undefined behavior. There are certain concepts such as sequence points, which you should be aware of when you are performing such operations.
Further, you can check this link also. I suggest you to move the increment of the operator after the pass to function. Then it should work fine.
You could avoid increasing the iterator at increment part of the loop and dot it in the body:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
match(*(*it), *(*(++it))); //Which parameter expression is evaluated first isn't determined
++it;
...
...
EDIT:
As FredOverflow has pointed out, match parameter expressions evaluations are not guaranteed to run in the left to right order. To avoid this hazardous situation:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
Team *pa = *it;
Team *pb = *(++it);
match(*pa, *pb);
++it;
...
...
you are increasing the iterator twice, first in the head of the for-loop:
std::advance(it,2)
then in the loop-body, where you do a:
++it
Is this really what you want? It looks pretty confusing to me.
If you want the element next to it, but dont want to increase it, better use:
auto nextIt = std::next(it);
Also: What does the match-function do? Are you sure its implemented right and not the source of the bug?
Hope this helps
Alexander

update map value

I have a map like this:
map<prmNode,vector<prmEdge>,prmNodeComparator> nodo2archi;
When I have to update the value (vector), I take the key and his value, I update the vector of values, I erase the old key and value then I insert the key and the new vector. The code is this:
bool prmPlanner::insert_edgemap(int from,int to) {
prmEdge e;
e.setFrom(from);
e.setTo(to);
map<prmNode,vector<prmEdge> >::iterator it;
for (it=nodo2archi.begin(); it!=nodo2archi.end(); it++){
vector<prmEdge> appo;
prmNode n;
n=(*it).first;
int indice=n.getIndex();
if (indice==f || indice==t){
appo.clear();
vector<prmEdge> incArchi;
incArchi=(*it).second;
appo=(incArchi);
appo.push_back(e);
nodo2archi.erase(it);
nodo2archi.insert(make_pair(n,appo) );
}
}
return true;
}
The problem is that for the first 40-50 iterations everything go weel and the map is updated well, while with more iterations it goes sometimes in segmentation fault, sometimes in an infinite idle. I don't know why. Somebody can help me please??
Thank you very much.
You are iterating through nodo2archi and at the sametime changing its size by doing nodo2archi.erase(it); and nodo2archi.insert(make_pair(n,appo) );. If you do that your iterator may become invalid and your it++ might crash.
Are you simply trying to append data to some of the mapped vectors? In this case you don't need to erase and insert anything:
for (MapType::iterator it = map.begin(); it != map.end(); ++it) {
if (some_condition) {
it->second.push_back(some_value);
}
}
The problem is that after erasing the iterator it you are trying to perform operations on it (increment) which is Undefined Behavior. Some of the answers state that modifying the container while you are iterating over it is UB, which is not true, but you must know when your iterators become invalidated.
For sequence containers, the erase operation will return a new valid iterator into the next element in the container, so this would be a correct and idiomatic way of erasing from such a container:
for ( SequenceContainer::iterator it = c.begin(); it != c.end(); )
// note: no iterator increment here
// note: no caching of the end iterator
{
if ( condition(*it) ) {
it = c.erase(it);
} else {
++it;
}
}
But sadly enough, in the current standard, associative containers erase does not return an iterator (this is fixed in the new standard draft), so you must manually fake it
for ( AssociativeContainer::iterator it = c.begin(); it != c.end(); )
// again, no increment in the loop and no caching of the end iterator
{
if ( condition(*it) ) {
AssociativeContainer::iterator del = it++; // increment while still valid
c.erase(del); // erase previous position
} else {
++it;
}
}
And even more sadly, the second approach, correct for associative containers, is not valid for some sequence containers (std::vector in particular), so there is no single solution for the problem and you must know what you are iterating over. At least until the next standard is published and compilers catch up.
Yo do modify collection while iterating over it.
You are erasing nodes while iterating through your map. This is asking for trouble :)
You must not modify a collection itself while iterating over it. C++ will allow it, but it still results in undefined behavior. Other languages like Java have fail-fast iterators that immediately break when the collection has been modified.