Traversing a list contaner excluding the last element - c++

I need to traverse a std::list until end() - 1 (so I don't want to include the last element when I traverse). What is an efficient way to do this?
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> l{1,2,3,4,5};
for(auto itr = l.begin(); itr != l.end() - 1; ++itr)
{
cout << *itr << '\n';
}
}

std::list::iterator is a BidirectionalIterator. You can use std::prev to get the iterator that you need to stop at.
for(auto itr = l.begin(), end = std::prev(l.end()); itr != end; ++itr)
{
...
}
See it working at https://ideone.com/26lJSC.

Regardless iterator is bidirectional or not the code would be like below:
for (auto itr2 = l.begin(), itr = l.end(); itr2 != l.end(); itr = itr2, ++itr2) {
if (itr != l.end()) {
cout << *itr << '\n';
}
}

With range-v3, it would be:
for (auto e : l | ranges::view::take(l.size() - 1))
{
std::cout << e << '\n';
}
Demo

Personally, for code that works also for uni-directional iterators, I would use an advance iterator, though there is initialisation complexity for if the list is empty :
for (it = l.begin(), ite = l.end(), itA = (it==ite)?it:std::next(it); itA != ite; it = itA, ++itA)
{
std::cout << e << '\n';
}
We are doing an extra copy per iteration, which the compiler may be able to optimise somewhat. It can't do much about the compare.
You can put the increment in the comparison for brevity, and when you KNOW the list is not empty, but I find that very unreadable:
if (!l.empty())
for (it = l.begin(), ite = l.end(), itA = it; ++itA != ite; it = itA)
{
std::cout << e << '\n';
}

Related

Calling erase() on an iterator, but not deleting correct element

I am learning c++ and I am working my way double linked lists, but I noticed something very peculiar when I was trying to delete elements from my list.
Problem: I am inserting an element before the value of 2 in my list , numbers, and then I am trying to delete any element that has the value 1.
Expected: After I call erase() in my conditional statement in my first loop my list, numbers, should get adjusted. The only values that numbers should should contain 0,1234,2,3.
Observed: My list numbers contains the values 0,1,1234,2,3. It is as if nothing was erased.
Code Example:
#include "stdafx.h"
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> numbers;
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_front(0);
list<int>::iterator it = numbers.begin();
it++;
numbers.insert(it, 100);
cout << "Element: " << *it << endl;
list<int>::iterator eraseIt = numbers.begin();
eraseIt++;
eraseIt = numbers.erase(eraseIt);
cout << "Element: " << *eraseIt << endl;
for (list<int>::iterator it = numbers.begin(); it != numbers.end(); it++)
{
if (*it == 2)
{
numbers.insert(it, 1234);
}
if (*it == 1)
{
it = numbers.erase(it);
}
else
{
it++;
}
}
for (list<int>::iterator it = numbers.begin(); it != numbers.end(); it++)
{
cout << *it << endl;
}
return 0;
}
I would greatly appreciate any assistance with this problem. Thank you for your time.
You should remove the it++ at the end of the for loop declaration, because it might be increased inside the for loop too; when it gets inscreased twice some elements will be skipped. i.e.
for (list<int>::iterator it = numbers.begin(); it != numbers.end(); )
LIVE
It's quite enough to remember the next iterator value before doing any deletion/insertion at the current position. This will help you to keep yourself careless about what and when are your modifications concretely doing.
list<int>::iterator it = numbers.begin();
while (it != numbers.end()) {
{
list<int>::iterator next_it = it;
++next_it;
// Do your insertion or deletion here
it = next_it;
}

Iterating multimap with conditional statement

First of all, I'm using C++98/03
I'm iterating my multimap starting from the second element:
multimap<pair<string, string>, pair<string, int> >::iterator it = paths.begin();
it++;
I have a conditional statement: if first element of first pair in current iterator is equal to the first element of first pair in a previous iterator, then do something, eg. print these elements.
for(; it != paths.end(); it++) {
if((*it).first.first == (*it--).first.first ) {
it++;
cout << (*it).first.first << " ";
cout << (*it--).first.first << endl;
it++;
}
else {
it++;
}
}
My question is how can I use a copy of an iterator instead of incrementing it back after every (*it--)?
Create an utility similar to C++11's std::prev:
#include <algorithm>
template <class T>
T prev(T it)
{
std::advance(it, -1);
return it;
}
Then use it as follows:
for(; it != paths.end(); it++) {
if((*it).first.first == prev(it)->first.first ) {
cout << (*it).first.first << " ";
cout << prev(it)->first.first << endl;
}
else {
it++;
}
}
Just use another iterator:
typedef multimap<pair<string, string>, pair<string, int> >::iterator iterator;
for( iterator it = paths.begin(); it != paths.end(); ) {
iterator prev = it++;
if( it == paths.end() )
break;
if( prev->first.first == it->first.first ) {
// output here
}
}
Note your code is incorrect, first of all it has UB as == is not sequenced. But even if you use different iterator on the left side, you would get wrong behaviour:
iterator it1 = it;
if((*it1).first.first == (*it--).first.first ) { // not UB anymore, but result is always true as you compare the same element

So is that possible to using iterator access all element in 2 dimensional vector just like pointer?

Look at following code (I know it's not right):
std::vector<std::vector<int>> ivec(2, std::vector<int>(9));
for (auto it = ivec.begin(); it != ivec.end(); it++)
{
for (auto itr = it.begin(); itr != it.end(); itr++)
{
std::cout << *itr << std::endl;
}
}
I've set up a 2 dimensional vector ivec. Now I want to access the each element using iterator instead subscripts. I know pointer is a special iterator. So is that possible to using iterator access all element in 2 dimensional vector just like pointer?
You asked:
So is that possible to using iterator access all element in 2 dimensional vector just like pointer?
Yes, it is.
The idea is sound. You have syntax errors in your posted code though.
std::vector<std::vector<int>> ivec(2, std::vector<int>(9));
for (auto it = ivec.begin(); it != ivec.end(); it++)
{
// for (auto itr = it.begin(); itr != it.end(); itr++)
for (auto itr = it->begin(); itr != it->end(); itr++)
{
std::cout << *itr << std::endl;
}
}
You can simplify that further using range-for loops.
std::vector<std::vector<int>> ivec(2, std::vector<int>(9));
for (auto& it : ivec)
{
for (auto itr : it)
{
std::cout << itr << std::endl;
}
}

inconsistent behavior with erasing map iterators in C++

I'm very confused by the behavior of the erase function for maps. In the simple example below, the code outputs "224". However, if you comment out the line "m['e'] = 5", it outputs "221". Neither result makes sense to me. Can someone explain the logic here?
#include <iostream>
#include <map>
using namespace std;
int main(){
map<char, int> m;
m['a'] = 1;
m['b'] = 2;
m['c'] = 3;
m['d'] = 4;
m['e'] = 5;
map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it);
cout << it->second;
it++;
cout << it->second << endl;
}
You cannot use an iterator after erasing it. It is invalid and the behaviour is not determined (crash, wriong value ?):
http://en.cppreference.com/w/cpp/container/map/erase
map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it); // (1)
cout << it->second; // (2)
it++; // (3)
cout << it->second << endl;
You have invalidated the iterator at position (1), so attempting to dereference it in (2) and (3) is undefined behavior. It is virtually identical to deleting a pointer and then then attempting to dereference it.
To erase elements in map for pre c++11 you can use this pattern:
for( map_type::iterator it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) map.erase( it++ );
else ++i;
}
Why map.erase( it++ ); works? Because it is basically equivalent to this code:
map::iterator tmp = it;
++it;
map.erase( tmp );
You should understand semantics of postfics/prefics operators if you want to use C++ effectively.
For c++11 you can also use this:
for( auto it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) it = map.erase( it );
else ++i;
}
I think in Visual C++ std::map::erase() also returned iterator, but that was not in standard.

How do you determine the last valid element in a STL-Container

If i iterate over a STL container i sometimes need to know if the current item is the last one in the sequence. Is there a better way, then doing something like this? Can i somehow convert rbegin()?
std::vector<int> myList;
// ....
std::vector<int>::iterator lastit = myList.end();
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != myList.end(); it++) {
if(it == lastit)
{
// Do something with last element
}
else
{
// Do something with all other elements
}
Try the following
std::vector<int>::iterator it2 = (++it);
if ( it2 == myList.end() ) {
...
}
The following should work as well
if ( it+1 == myList.end() ) {
// it is last
...
}
Maybe you can iterate backwards (use rbegin/rend) and put your special task before the loop or replace the end check with it != lastit and put the special handling after the loop
I would have some doubts about my design if some elements need to be treated differntly, but this suggestion is a bit cleaner for me (don't forget to test for empty containers)
std::vector<int>::iterator lastit = myList.end();
if (lastit != myList.begin())
{
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != lastit; ++it)
{
// Do
}
// Do with last
}
Use reversed iteration, this way you will have only one end()-1-like computation (notice the rbegin()+1) and no comparsions:
for(vector<int>::iterator it = myValues.rbegin()+1; it != myValues.rend(); it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
Also, for the vector<>, computing end()-1 is probably fast, so you can also do it like following:
for(vector<int>::iterator it = myValues.begin(); it != myValues.end()-1; it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
If you don't want to process the element after the loop, you can:
for(vector<int>::iterator it = myValues.rbegin(); it != myValues.rend(); it++) {
if(it == myValues.rbegin())
cout << "Process last one: " << *it << endl;
else
cout << *it << endl;
}
For a random access iterator like that for vector, you don't need the temporarary. You can say:
if ( it + 1 == v.end() ) {
// at one before end
}
Edit: And even for non-random access types one could use std:;distance:
if ( distance( it, v.end() ) == 1 ) {
// at one before end
}
An important question is: why create a loop if you do something special for 1 element. Why not do something special to the 3rd element? To every 4rth? ...
Just iterate over the elements to be treated the same, write separate code to treat the others.
Have a look at answers to this question, too.
Why not:
if(!myList.empty())
last_it = myList.begin() + myList.size()-1;
else
last_it = myList.end();
//or
last_it = myList.empty() ? myList.end() : myList.begin() + myList.size() - 1;
If you're using a vector, it's actually much simpler to use an integer index to iterate:
std::vector<int> myList;
for (unsigned int i = 0; i < myList.size(); i++)
{
if (i == (myList.size() - 1))
{
processDifferently (myList[i])
}
else
{
process (myList[i])
}
}
Minimizing the number of calls to myList.size() is left as an exercise for the OP :)