tony
tony

Reputation: 81

How can I recognize the last iteration in a C++ while loop?

How would I make so that the last player name doesn't have a , so it's:

Player online:
Jim, John, Tony

and not

Player online:
Jim, John, Tony,

My code is:

bool Commands::whoIsOnline(Creature* c, const std::string &cmd, const std::string &param)
{
Player* player = dynamic_cast<Player*>(c);

if (player)
{
    player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Players online: ");
    AutoList<Player>::listiterator iter = Player::listPlayer.list.begin();
    std::string info;
    int count = 0;

    while (iter != Player::listPlayer.list.end())
    {
        info += (*iter).second->getName() + ", ";
        ++iter;
        ++count;

        if (count % 10 == 0)
        {
            player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, info.c_str());
            info.clear();
        }
    }

    if (!info.empty())
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, info.c_str());
}

return true;
}

Upvotes: 8

Views: 3704

Answers (11)

Liao
Liao

Reputation: 1265

...    
std::string info;
...
while (iter != Player::listPlayer.list.end())
{
  if(info.size() > 0)
    info += ",";
  info += (*iter).second->getName();
  ......  
}

Upvotes: 0

fredoverflow
fredoverflow

Reputation: 263138

My solution involves a variable that starts out as the empty string and is set to ", " after each iteration (which only has an effect after the first iteration). No special cases need to be checked.

template<class ForwardIterator>
std::string sequence_to_string(ForwardIterator begin, ForwardIterator end)
{
    std::string output;
    const char* delimiter = "";
    for (ForwardIterator it = begin; it != end; ++it)
    {
        output += delimiter;
        output += *it;
        delimiter = ", ";
    }
    return output;
}

Upvotes: 1

Potatoswatter
Potatoswatter

Reputation: 137810

Instead of finding the last iteration, find the first iteration. Handle special cases at the beginning of the loop, have a definite "clean" state before doing the "real work," and perform the increment at the end.

while (iter != Player::listPlayer.list.end())
{
    if ( count != 0 )
    {
        info += ", ";

        if (count % 10 == 0)
        {
            player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, info.c_str());
            info.clear();
        }
    }
    // invariant: info is clean and ready to accept data

    info += (*iter).second->getName();
    ++iter;
    ++count;
}

Upvotes: 1

JRL
JRL

Reputation: 78003

You can use string join from .NET or Boost or some other library or write your own. Although it might be overkill for that particular function, it's the kind of thing that you'll probably use elsewhere in that project, and that you'll definitely reuse in another project.

Upvotes: 2

Mark Ransom
Mark Ransom

Reputation: 308206

If this were my code, I'd probably just check the string at the beginning of the loop and add the comma when it's not empty. It's nice to know how to handle similar situations when that workaround is not available, so here's an alternate:

while (iter != Player::listPlayer.list.end())
{
    info += (*iter).second->getName();
    ++iter;
    if (iter != Player::listPlayer.list.end())
        info += ", ";
    ++count;
    ...
}

Upvotes: 1

Ponkadoodle
Ponkadoodle

Reputation: 5957

Instead of thinking it like player + "," think of it as "," + player

So you could do something like this (psuedo-code):

onFirstName = true
output = ""
for each player in players:
    if onFirstName:
        onFirstName = false
    else:
        output += ", "
    output += player's name

of if your language supports it (Which c++ does):

if length of players > 0:
    output = players[0]
    for each player in players except players[0]:
        output += ", " + player's name
else:
    output = ""

I like the look of that last one, I'll have to invent a language that actually works like that.

Upvotes: 8

Mark Elliot
Mark Elliot

Reputation: 77044

change

while(iter != Player::listPlayer.list.end())
{
    info += (*iter).second->getName() + ", ";
//...

with:

if(iter != Player::listPlayer.list.end()){
    info += (*iter).second->getName();
    ++iter;
    while(iter != Player::listPlayer.list.end()){
    {
        info += ", " + (*iter).second->getName();     
        //...
    }
    //...
}

alternatively, you can do something like this if you don't want the comma in front of a name after the info.clear():

while(iter != Player::listPlayer.list.end())
{
    info += ", " + (*iter).second->getName();
    // ...
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, info.c_str()+2);

Upvotes: 4

jamesdlin
jamesdlin

Reputation: 89995

I wrote some sample code awhile ago to demonstrate a few different ways of doing this in C:

http://www.taenarum.com/csua/fun-with-c/delimiter.c

Unfortunately, there is no method that is clearly superior to the others. I personally would go with a conventional approach (explicitly check for either the first or last element) for clarity and to avoid duplicating code. (And definitely avoid using the goto version in C++ code.)

Upvotes: 0

Carlos Guti&#233;rrez
Carlos Guti&#233;rrez

Reputation: 14282

(Borrowing wallacoloo's pseudocode)

output = "" 
for each player in players: 
    if output != "" 
        output += ", " 
    output += player's name 

Upvotes: 3

David Gladfelter
David Gladfelter

Reputation: 4213

If this is C++ and that is an STL iterator, then if the iterator is a random-access iterator, then you could actually ask

if (iter + 1 == Plaer::listPlayer.list.end())

If you're not allowed to do that, then you probably want to put the code inside the while loop that prints a player's name in a separate function and call that function on the first element before the while loop, then call it inside the while loop. Then put the code that prints the comma before the call to the player name print in the while loop. That way the first call will print just the first name, and then the while loop will always first print a comma and then the player's name, so that the output always ends with a player's name.

Upvotes: 0

sth
sth

Reputation: 229623

The easiest way is to simply remove the additional ", " in the end:

if (!info.empty()) {
  info.erase(info.size()-2);
}

Upvotes: 2

Related Questions