iterate through two std::lists simultaneously - c++

Sorry if this is too simple a question.
Prior error checking ensures l1.size() == l2.size().
std::list<object1>::iterator it1 = l1.begin();
std::list<object2>::iterator it2 = l2.begin();
while(it1 != l1.end() && it2 != l2.end()){
//run some code
it1++;
it2++;
}
Is this a reasonable approach, or is there a more elegant solution? Thanks for your help.

I prefer to use for if increments unconditionally occurs:
for(; it1 != l1.end() && it2 != l2.end(); ++it1, ++it2)
{
//run some code
}
You can omit one test while the size of lists are the same, but I'm not sure what's going on in run some code!

I think this is perfectly reasonable (except that I'd use pre-increment rather than post-increment).
You could consider using a "zip iterator" of some sort, but it's not totally obvious that this would be worth the hassle in this case.

If you are doing a simple operation on each pair of objects, you can use std::transform.

It is reasonable to do it the way you have, there are some other approaches you could take to minimise the amount of checks being done:
If you have already checked both lengths are equal (as stated as a prior check), a standard for loop may well suffice, which eliminates the access of two variables and relies only on the increment of one variable:
for (int i = 0; i< l1.size();i++)
{
// run some code here
}
However you would need to use advance() or next() to march through the objects in the list within the "some code here".

Related

How does erase work on a vector? [duplicate]

I know that there are similar questions to this one, but I didn’t manage to find the way on my code by their aid. I want merely to delete/remove an element of a vector by checking an attribute of this element inside a loop. How can I do that? I tried the following code but I receive the vague message of error:
'operator =' function is unavailable in 'Player’.
for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
What should I do?
Update: Do you think that the question vector::erase with pointer member pertains to the same problem? Do I need hence an assignment operator? Why?
You should not increment it in the for loop:
for (vector<Player>::iterator it=allPlayers.begin();
it!=allPlayers.end();
/*it++*/) <----------- I commented it.
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
Notice the commented part;it++ is not needed there, as it is getting incremented in the for-body itself.
As for the error "'operator =' function is unavailable in 'Player’", it comes from the usage of erase() which internally uses operator= to move elements in the vector. In order to use erase(), the objects of class Player must be assignable, which means you need to implement operator= for Player class.
Anyway, you should avoid raw loop1 as much as possible and should prefer to use algorithms instead. In this case, the popular Erase-Remove Idiom can simplify what you're doing.
allPlayers.erase(
std::remove_if(
allPlayers.begin(),
allPlayers.end(),
[](Player const & p) { return p.getpMoney() <= 0; }
),
allPlayers.end()
);
1. It's one of the best talks by Sean Parent that I've ever watched.
if(allPlayers.empty() == false) {
for(int i = allPlayers.size() - 1; i >= 0; i--) {
if(allPlayers.at(i).getpMoney() <= 0) {
allPlayers.erase( allPlayers.begin() + i );
}
}
}
This is my way to remove elements in vector.
It's easy to understand and doesn't need any tricks.
Forget the loop and use the std or boost range algorthims.
Using Boost.Range en Lambda it would look like this:
boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );
Your specific problem is that your Player class does not have an assignment operator. You must make "Player" either copyable or movable in order to remove it from a vector. This is due to that vector needs to be contiguous and therefore needs to reorder elements in order to fill gaps created when you remove elements.
Also:
Use std algorithm
allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
return player.getpMoney() <= 0;
}), allPlayers.end());
or even simpler if you have boost:
boost::remove_erase_if(allPlayers, [](const Player& player)
{
return player.getpMoney() <= 0;
});
See TimW's answer if you don't have support for C++11 lambdas.
Or do the loop backwards.
for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
C++11 has introduced a new collection of functions that will be of use here.
allPlayers.erase(
std::remove_if(allPlayers.begin(), allPlayers.end(),
[](auto& x) {return x->getpMoney() <= 0;} ),
allPlayers.end());
And then you get the advantage of not having to do quite so much shifting of end elements.
Late answer, but as having seen inefficient variants:
std::remove or std::remove_if is the way to go.
If for any reason those are not available or cannot be used for whatever other reason, do what these hide away from you.
Code for removing elements efficiently:
auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
if(isKeepElement(*i)) // whatever condition...
{
if(i != pos)
{
*pos = *i; // will move, if move assignment is available...
}
++pos;
}
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());
You might need to write such a loop explicitly e. g. if you need the iterator itself to determine if the element is to be removed (the condition parameter needs to accept a reference to element, remember?), e. g. due to specific relationship to successor/predecessor (if this relationship is equality, though, there is std::unique).

C++ erase last element of a vector using erase(Iterator) [duplicate]

I know that there are similar questions to this one, but I didn’t manage to find the way on my code by their aid. I want merely to delete/remove an element of a vector by checking an attribute of this element inside a loop. How can I do that? I tried the following code but I receive the vague message of error:
'operator =' function is unavailable in 'Player’.
for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
What should I do?
Update: Do you think that the question vector::erase with pointer member pertains to the same problem? Do I need hence an assignment operator? Why?
You should not increment it in the for loop:
for (vector<Player>::iterator it=allPlayers.begin();
it!=allPlayers.end();
/*it++*/) <----------- I commented it.
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
Notice the commented part;it++ is not needed there, as it is getting incremented in the for-body itself.
As for the error "'operator =' function is unavailable in 'Player’", it comes from the usage of erase() which internally uses operator= to move elements in the vector. In order to use erase(), the objects of class Player must be assignable, which means you need to implement operator= for Player class.
Anyway, you should avoid raw loop1 as much as possible and should prefer to use algorithms instead. In this case, the popular Erase-Remove Idiom can simplify what you're doing.
allPlayers.erase(
std::remove_if(
allPlayers.begin(),
allPlayers.end(),
[](Player const & p) { return p.getpMoney() <= 0; }
),
allPlayers.end()
);
1. It's one of the best talks by Sean Parent that I've ever watched.
if(allPlayers.empty() == false) {
for(int i = allPlayers.size() - 1; i >= 0; i--) {
if(allPlayers.at(i).getpMoney() <= 0) {
allPlayers.erase( allPlayers.begin() + i );
}
}
}
This is my way to remove elements in vector.
It's easy to understand and doesn't need any tricks.
Forget the loop and use the std or boost range algorthims.
Using Boost.Range en Lambda it would look like this:
boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );
Your specific problem is that your Player class does not have an assignment operator. You must make "Player" either copyable or movable in order to remove it from a vector. This is due to that vector needs to be contiguous and therefore needs to reorder elements in order to fill gaps created when you remove elements.
Also:
Use std algorithm
allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
return player.getpMoney() <= 0;
}), allPlayers.end());
or even simpler if you have boost:
boost::remove_erase_if(allPlayers, [](const Player& player)
{
return player.getpMoney() <= 0;
});
See TimW's answer if you don't have support for C++11 lambdas.
Or do the loop backwards.
for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
C++11 has introduced a new collection of functions that will be of use here.
allPlayers.erase(
std::remove_if(allPlayers.begin(), allPlayers.end(),
[](auto& x) {return x->getpMoney() <= 0;} ),
allPlayers.end());
And then you get the advantage of not having to do quite so much shifting of end elements.
Late answer, but as having seen inefficient variants:
std::remove or std::remove_if is the way to go.
If for any reason those are not available or cannot be used for whatever other reason, do what these hide away from you.
Code for removing elements efficiently:
auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
if(isKeepElement(*i)) // whatever condition...
{
if(i != pos)
{
*pos = *i; // will move, if move assignment is available...
}
++pos;
}
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());
You might need to write such a loop explicitly e. g. if you need the iterator itself to determine if the element is to be removed (the condition parameter needs to accept a reference to element, remember?), e. g. due to specific relationship to successor/predecessor (if this relationship is equality, though, there is std::unique).

How to remove common values from two string lists

I'm new to C++ and after googling quite a bit I haven't found a solution.
I'm making a Hexgame, and I'm trying to check if there is a winner.
My function checkpath, gets a list of places it has visited (usedPlaces), and creates the list Possible, of places it can check next from the current position.
So I want to delete the places I have already visited from Possible.
The error I'm getting atm is "List iterators incompatible"
I've tried things, but they mostly end up giving me other errors
for (list<string>::iterator it = possible.begin(); it != possible.end(); it++)
{
for (list<string>::iterator it2 = usedPlaces.begin(); it2 != usedPlaces.end();)
{
if (it == it2)
{
possible.remove(*it);
}
else
{
it2++;
}
}
}
When you do this:
if (it == it2)
you're comparing an iterator into one list to an iterator into another list. Those would never compare equal anyway, and it's handy that your implementation debugs this for you. Even if you fixed the comparison (to *it == *it2), the code would still be buggy due to how and when it gets incremented and inefficient (due to the extra searching of possible). A functional version would be:
for (list<string>::iterator it = possible.begin(); it != possible.end(); /* nothing */)
{
list<string>::iterator it2 = std::find(usedPlaces.begin(), usedPlaces.end(), *it);
if (it2 != usedPlaces.end()) {
it = possible.erase(it);
}
else {
++it;
}
}
But this is complicated to write and error-prone. I'd prefer to use list::remove_if, which takes a predicate and removes all the elements for which that predicate returns true:
possible.remove_if([&](const std::string& place){
return std::find(usedPlaces.begin(), usedPlaces.end(), place)
!= usedPlaces.end();
});
That's much more direct.
The goal is to removes string objects that are the same, so the comparison should look at the string objects. Instead of
if (it == it2)
it should be
if (*it == *it2)
The operation it == it2 checks if one iterator points to the same element as the other iterator. That is never true, because those two iterators are coming from two different lists.
What you probably want to compare are the contents of each list element, i.e. *it.
Note, there may be other problems in your code. E.g. after you call possible.remove(*it) your iterator it is no longer referencing any elements of the list, therefore you won't be able to increment it in the next iteration of the loop.
Consider using possible.erase and getting its result. You will need, most likely, change your outher loop though.

Is there difference between two for form in C++?

vector<int> a;
1.
for(vector<int>::iterator it = a.begin(); it != a.end(); ++it)
2.
vector<int>::iterator end = a.end();
for(vector<int>::iterator it = a.begin(); it != end; ++it)
which is more efficient?or the same?
Initial criticisms:
1/ Typical tutorial example
for(vector<int>::iterator it = a.begin(); it != a.end(); ++it)
There is no magic, but it brings up a question: is a ever modified in the loop that the end bound may vary ?
2/ Improved
vector<int>::iterator end = a.end();
for(vector<int>::iterator it = a.begin(); it != end; ++it)
a.end() is only executed once, it seems. However since end is not const, it may be modified inside the loop.
Furthermore, it introduces the end identifier in the outer scope, polluting it.
So there is a potential gain in performance, but not much in clarity. Also, it's far more verbose.
I would propose several other ways:
3/ Best Manual
for(vector<int>::iterator it = a.begin(), end = a.end(); it != end; ++it)
Combines the advantages of v1 (quite terse, no outer scope pollution) and v2 (performance), however it is still unclear if end is ever modified within the loop body.
4/ Boost-powered
BOOST_FOREACH(int& i, a)
Even terser than v1, immediately identifiable at a glance, no outer scope leak, and guarantee of full iteration (it's not possible to modify the bounds).
Unfortunately:
there are issues with commas in the type of the variable (because it relies on the preprocessor)
compile-time errors are completely cryptic (because it relies on the preprocessor)
Note: in theory, one could make the case of the std::foreach algorithm here, but honestly... there is too much effort involved in defining a predicate outside and it breaks code locality.
5/ C++11 range-for statement
for (int& i: a)
All the advantages:
Extremely Terse
As performant as the best C++ hand-written loop
Guaranteed full iteration, no questions asked
And none of the issues (scope leak, preprocessor magic).
Personally, I use C++11 range-for whenever I can (hobby projects) and BOOST_FOREACH otherwise (at work).
I avoid like the plague modifying the container I am iterating on, preferring to rely on STL algorithms when I need to filter/remove elements... It's too easy to mess up with the boundary conditions and iterator invalidations otherwise.
2nd is more efficient as it only requires creating the end iterator once.
A smart compiler may optimize the first one to be the second, but you cannot be guaranteed that that will happen.
It would actually be a bit of a complicated optimization because the compiler would need to be 100% certain that any subsequent call to end() will have no additional effects or return anything different. Basically, it would need to know that at least over the loop, end() always returns something such that end() == previous call to end(). Whether or not compilers do that optimization is not guaranteed.
2nd way is obviously better, as it calls a.end() only once. In essence if there are N nodes in your tree then you save N calls to a.end().
I think that the first for loop is more certain. In case you insert/erase elements inside this for loop the end iterator you have defined is invalidated. For example:
vector<int>::iterator mend = int_vec.end(), mbegin = int_vec.begin();
while(mbegin != mend)
{
cout << *mbegin << " ";
int_vec.erase(mbegin);
// mbegin is automatically invalidated
// execution of this program causes bizarre runtime_error !
// never try this at home !
}
A safer version of the code above could be this:
vector<int>::iterator mend = int_vec.end(), mbegin = int_vec.begin();
while(mbegin != mend)
{
cout << *mbegin << " ";
int_vec.erase(mbegin);
mbegin = int_vec.begin(); // ok, mbegin updated.
}

Remove elements of a vector inside the loop

I know that there are similar questions to this one, but I didn’t manage to find the way on my code by their aid. I want merely to delete/remove an element of a vector by checking an attribute of this element inside a loop. How can I do that? I tried the following code but I receive the vague message of error:
'operator =' function is unavailable in 'Player’.
for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
What should I do?
Update: Do you think that the question vector::erase with pointer member pertains to the same problem? Do I need hence an assignment operator? Why?
You should not increment it in the for loop:
for (vector<Player>::iterator it=allPlayers.begin();
it!=allPlayers.end();
/*it++*/) <----------- I commented it.
{
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
else
++it;
}
Notice the commented part;it++ is not needed there, as it is getting incremented in the for-body itself.
As for the error "'operator =' function is unavailable in 'Player’", it comes from the usage of erase() which internally uses operator= to move elements in the vector. In order to use erase(), the objects of class Player must be assignable, which means you need to implement operator= for Player class.
Anyway, you should avoid raw loop1 as much as possible and should prefer to use algorithms instead. In this case, the popular Erase-Remove Idiom can simplify what you're doing.
allPlayers.erase(
std::remove_if(
allPlayers.begin(),
allPlayers.end(),
[](Player const & p) { return p.getpMoney() <= 0; }
),
allPlayers.end()
);
1. It's one of the best talks by Sean Parent that I've ever watched.
if(allPlayers.empty() == false) {
for(int i = allPlayers.size() - 1; i >= 0; i--) {
if(allPlayers.at(i).getpMoney() <= 0) {
allPlayers.erase( allPlayers.begin() + i );
}
}
}
This is my way to remove elements in vector.
It's easy to understand and doesn't need any tricks.
Forget the loop and use the std or boost range algorthims.
Using Boost.Range en Lambda it would look like this:
boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );
Your specific problem is that your Player class does not have an assignment operator. You must make "Player" either copyable or movable in order to remove it from a vector. This is due to that vector needs to be contiguous and therefore needs to reorder elements in order to fill gaps created when you remove elements.
Also:
Use std algorithm
allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
return player.getpMoney() <= 0;
}), allPlayers.end());
or even simpler if you have boost:
boost::remove_erase_if(allPlayers, [](const Player& player)
{
return player.getpMoney() <= 0;
});
See TimW's answer if you don't have support for C++11 lambdas.
Or do the loop backwards.
for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
if(it->getpMoney()<=0)
it = allPlayers.erase(it);
C++11 has introduced a new collection of functions that will be of use here.
allPlayers.erase(
std::remove_if(allPlayers.begin(), allPlayers.end(),
[](auto& x) {return x->getpMoney() <= 0;} ),
allPlayers.end());
And then you get the advantage of not having to do quite so much shifting of end elements.
Late answer, but as having seen inefficient variants:
std::remove or std::remove_if is the way to go.
If for any reason those are not available or cannot be used for whatever other reason, do what these hide away from you.
Code for removing elements efficiently:
auto pos = container.begin();
for(auto i = container.begin(); i != container.end(); ++i)
{
if(isKeepElement(*i)) // whatever condition...
{
if(i != pos)
{
*pos = *i; // will move, if move assignment is available...
}
++pos;
}
}
// well, std::remove(_if) stops here...
container.erase(pos, container.end());
You might need to write such a loop explicitly e. g. if you need the iterator itself to determine if the element is to be removed (the condition parameter needs to accept a reference to element, remember?), e. g. due to specific relationship to successor/predecessor (if this relationship is equality, though, there is std::unique).