tags:

views:

135

answers:

7

How to do the following in more stylish/short way?

for(i=container.begin(); i!=container.end(); ++i) {
    if (i!=container.begin()) {
        cout << ", ";
    }
    cout << *i;
    j=i;
    if (++j==container.end()) {
        cout << "!" << endl; 
    }
}

Solutions like foreach are acceptable (actions on first and last elements need to be configurable, though).

P.S. There are many answers that are handling first element, but not last. Here is what I mean by handling last element:

for(i=container.begin(); i!=container.end(); ++i) {
    j=i;
    if (i==container.begin()) {
        cout << "[" << *i << "]" << endl;
    } else if (++j==container.end()) {
        cout << ", (" << *i << ")" << "!" << endl; 
    } else {
         cout << ", " << *i;
    }
}

Don't you think it's very easy to handle first element outside the cycle body? The real problem is the last one! I'm sorry for not being able to clarify the important point asking the question. I think I'll just accept the top ranked answer eventually.

+2  A: 

In your code,

if (i==container.end()) {
    cout << "!" << endl; 
}

will never happen.

My own approach would be to use the container size (I think size() is now constant time for all Standard Library containers). Maintain a count in the loop and you are at the end when count == size() - 1, and at the beginning when count == 0, obviously.

anon
Fixed an error in question. Thank you.
Basilevs
i thought currently the standard only guaranteed O(n) for size for non random access containers? i'd still probably go for this unless profiling showed it was an issue
jk
@jk There was a discussion about this here awhile back (which of course I can't find) - the only container which historically has not been O(1) for size is std::list, but there seems to have been a TC or something changing this - sorry not to be more helpful, perhaps someone else can confirm or deny this authoritatively?
anon
+1  A: 

Shift the ++i a bit:

i = container.begin();
while(i != container.end()) {
    if (i != container.begin()) {
        cout << ", ";
    }
    cout << *i;
    if (++i == container.end()) {
        cout << "!" << endl; 
    }
}
Mike DeSimone
Nice. Removing cycle condition and inserting break in the last if statement block would be good too.
Basilevs
+3  A: 

My advice here would be: there is no point in detecting anything within this loop !

Since your special cases are at the beginning and the end of your container, it is easy to remove their processing from within the loop.

The following function will print the contents of any container class whose elements can be <<'ed to an std::ostream:

template < class Container >
void print(Container const & container)
{
    typename Container::const_iterator current = container.begin();
    typename Container::const_iterator const end = container.end();
    if (current != end)
    {
        std::cout << *current;
        for (++current; current != end; ++current)
        { 
            std::cout << ", " << *current;
        }
        std::cout << "!" << std::endl;
    }
}
bltxd
For cycle is better than while. The handling of the last element is missing though.
Basilevs
No, last element is processed correctly. end() return an iterator which shall never be dereferenced, it's past the end. Unless you intended not to print the '!' ?
bltxd
My example don't demonstrate this, but the question clearly has requested special handling of the last element. I don't mean *(container.end()) here. I mean last valid element.
Basilevs
Hmmm the last element gets followed by a '!' if that's what you ask. If you call it on [1,2,3] you'll get "1, 2, 3!"... What did I miss ?
bltxd
I'm sorry for my example, that really don't handle the last element in any way. I've edited the question adding a new example.
Basilevs
+1  A: 
template < class TContainerType>
void print(TContainerType const & i_container)
  {
  typename TContainerTypeconst ::const_iterator current = i_container.begin();
  typename TContainerTypeconst ::const_iterator const end = i_container.end();
  if(current != end)
    {
    std::cout << *current++;
    while(current != end)
      std::cout << ", " << *current++;
     }
  std::cout << "!" << std::endl;
  }
MadKeithV
+2  A: 

As container is not defined by you, I used the simplest - vector

template <class T>
string vector_join( const vector<T>& v, const string& token ){
  ostringstream result;
  for (typename vector<T>::const_iterator i = v.begin(); i != v.end(); i++){
    if (i != v.begin()) result << token;
    result << *i;
  }
  return result.str();
}

//usage
cout << vector_join( container, ", " ) << "!";
Draco Ater
+2  A: 

Boost has next / prior which can sometimes help in such situations.

for(i=container.begin(); i!=container.end(); ++i) {
    if (boost::next(i) == container.end()) {
         std::cout << "!" << std::endl;
    }
}

Although for this specific case, I'd simply output the first element, loop from second till last while always outputting the ',' and then output the '!' after the loop has ended. (as others have suggested already)

I don't see the point in moving the special cases inside the loop, and then checking inside the loop for them....

Pieter
+1 for boost::next education
ceretullis
This is the best answer from my point of view. See updated question with clarification.
Basilevs
A: 

Take the second part out of the loop.

for(i=container.begin(); i!=container.end(); ++i) {
    if (i != container.begin()) {
        cout << ", ";
    }
    cout << *i;
}
cout << "!" << endl; 
grokus
How do I use the last element then? i after the cycle points to end() which don't point to the last element.
Basilevs