Last row in foreach loop

  • Thread starter Thread starter tshad
  • Start date Start date
Rene said:
Besides what others have mentioned, here is another way to tackle your
problem without having to have a counter in case you had something against
counters.


Racecar lastRacecar;
foreach (Racecar racecar in RaceCarCollection)
{
lastRacecar = racecar;

// Do something...........
}
if(lastRacecar != null)
{
// Do last row stuff here...........
}
Huh?

How would racecar ever be null?

lastRaceCar would always be equal to the current row.

Tom
 
Family Tree Mike said:
tshad said:
Is there a way to know if you are looking at the last record record of
foreach loop other then setting up a loop counter that you manually
increment?

foreach (Racecar racecar in RaceCarCollection)
{
...

if last row do something?
}

You can tell how many items you have in the collection but is there way
to
tell which row you are looking or if this is the last row?

Thanks,

Tom

Won't the following work? I can't say it's efficient...

if (racecar == RaceCarCollection [RaceCarCollection.count - 1])
{
// do something...
}

Actually, that might work.

Thanks,

Tom
 
Peter Morris said:
What occurs to me is that you only want something done once, so why put it
in the loop at all?

foreach (RaceCar currentRaceCar in RaceCarCollection)
currentRaceCar.Start();

RaceCarCollection[RaceCarCollection.Count - 1].BreakDown();

What does BreakDown do?

Thanks,

Tom
 
Huh?

How would racecar ever be null?

You're right, it wouldn't. Not with the code written that way. Of
course, with it written that way, it won't compile either because
"lastReacecar" is never definitively assigned before being used.

If you fix the compiler error and assign null to the local variable before
the foreach() loop, then the variable will be null if your collection is
empty.
lastRaceCar would always be equal to the current row.

What's its value if there is no "current row"?

If the code is written such that it compiles, the value is "null".

Pete
 
Huh?

How would racecar ever be null?

You're right, it wouldn't. Not with the code written that way. Of
course, with it written that way, it won't compile either because
"lastReacecar" is never definitively assigned before being used.
Right.

It would have to be:

Racecar lastRacecar = null;

as you mention below.
If you fix the compiler error and assign null to the local variable before
the foreach() loop, then the variable will be null if your collection is
empty.
Yes, but in my case, I was only assuming that there was at least one item in
the collection and I wanted to make sure the last line gets processed
because I could be accumulating something and in the last row I need to make
sure I process it - before leaving the loop.
What's its value if there is no "current row"?
If you are inside the loop (at least one item), there would be a current
row.
If the code is written such that it compiles, the value is "null".

Only if there is no items in collection.

Thanks,

Tom
 
[...]
If you fix the compiler error and assign null to the local variable
before
the foreach() loop, then the variable will be null if your collection is
empty.
Yes, but in my case, I was only assuming that there was at least one
item in
the collection and I wanted to make sure the last line gets processed
because I could be accumulating something and in the last row I need to
make
sure I process it - before leaving the loop.

Nothing about what you wrote causes me to believe that tracking the last
item as suggested by Rene would not work. To "accumulate" something, you
must be using a variable declared outside the loop anyway, and if at the
end of the loop you intend to do something with that "accumulated"
something relative to the last item enumerated in the loop, you can simply
do that outside of the loop, per Rene's suggestion.

It's possible Rene's suggestion wouldn't work for you, but if so you
haven't explained why it wouldn't with the paragraph I quoted above.

As far as your comment about the check for null goes, the code Rene posted
was simply being cautious. You may decide to make the assumption that the
variable will never be null, and thus leave that check out. I personally
wouldn't make the assumption, but you're free to do so if you like.
Whether you do or not doesn't change the validity of the suggestion.

Pete
 
tshad skrev:
I could.

But it wasn't a problem just a question.

At the moment, I just set iktr= 0 and just a line after the foreach line
(iktr++;) and then test against RaceCarCollection.Count as you mentioned.

I was just curious if there was a property somewhere that told you what line
in the collection you were on without having to do this.

Depends on what kind of object you have and how it is organized.
In your example: Is Racecar.Index an option?
 
Bjørn Brox said:
tshad skrev:

Depends on what kind of object you have and how it is organized.
In your example: Is Racecar.Index an option?

I don't know.

What is that?

Tom.
 
Peter Duniho said:
[...]
If you fix the compiler error and assign null to the local variable
before
the foreach() loop, then the variable will be null if your collection is
empty.
Yes, but in my case, I was only assuming that there was at least one
item in
the collection and I wanted to make sure the last line gets processed
because I could be accumulating something and in the last row I need to
make
sure I process it - before leaving the loop.

Nothing about what you wrote causes me to believe that tracking the last
item as suggested by Rene would not work. To "accumulate" something, you
must be using a variable declared outside the loop anyway, and if at the
end of the loop you intend to do something with that "accumulated"
something relative to the last item enumerated in the loop, you can simply
do that outside of the loop, per Rene's suggestion.

I agree. I may not have been clear on that issue. I wasn't being critical
here, just trying to understand it.
It's possible Rene's suggestion wouldn't work for you, but if so you
haven't explained why it wouldn't with the paragraph I quoted above.
In my example, I want to be able to do something on the last loop and
process it (as I do on the previous loops) without having to rewrite the
processing code outside of the loop:

int ktr = 0;
foreach (Racecar racecar in RaceCarCollection)
{
ktr++;
// Do something...........

if(ktr == RaceCarCollection.Count)
{
// Do last row stuff here...........
}
// Do something else
}

As Rene said I could put the null test outside of the foreach loop, but then
I have to do the "// Do something else" code outside as well.
As far as your comment about the check for null goes, the code Rene posted
was simply being cautious. You may decide to make the assumption that the
variable will never be null, and thus leave that check out. I personally
wouldn't make the assumption, but you're free to do so if you like.
Whether you do or not doesn't change the validity of the suggestion.
That's true.

Thanks,

Tom
 
[...]
In my example, I want to be able to do something on the last loop and
process it (as I do on the previous loops) without having to rewrite the
processing code outside of the loop:

So, if I understand correctly, not only do you want to do something
special when you reach the last element of the loop, the processing of
that last element is supposed to incorporate that "something special".

In that case, yes...there's a benefit to keeping that inside the loop and
tracking a counter may be a better solution. Noting, of course, that
that's mainly because you've said you do have a valid count for the
collection and so it's reasonable to make that test without any additional
work.

One remaining point: if your processing is some significant amount of code
(more than a couple of lines, for example), then I think it would be
almost as reasonable to put the processing into a separate method and then
call it from two places: inside the loop, and then at the end.

I say "almost", because doing that actually makes the loop more awkward.
It'd have to look something like this:

Racecar racecarPrev = null;

foreach (Racecar racecar in RaceCarCollection)
{
if (racecarPrev != null)
{
ProcessItem(racecarPrev);
}

racecarPrev = racecar;
}

if (racecarPrev != null)
{
// do "something special"
ProcessItem(racecarPrev);
}

That way you don't do the "ProcessItem" work until you know whether the
item is in fact the last one or not. Since the processing is encapsulated
in a separate method, I think it's less of a problem to duplicate that
call (I definitely don't like copy-and-pasting code that does real work,
but duplicating a simple call to a method isn't so bad if it's otherwise
beneficial).

Obviously you wouldn't contort your code to work like the above if you
already have a valid count for your collection and can do the "last item"
test inside the loop. But it could be worth keeping the above arrangement
in mind for dealing with collections that don't expose a count.

Pete
 
[...]
Depends on what kind of object you have and how it is organized.
In your example: Is Racecar.Index an option?

I don't know.

What is that?

I think Bjørn is asking whether your Racecar class itself keeps track of
its own position within the collection.

Some collections are like this. The item in the collection can only be in
one collection at a time, and it does know its own position within the
collection. I would guess, based on previous examples where you've used
this same class and related collections, that this isn't the case for
you. But if it were, you could look at the item's position rather than
tracking a separate index.

But no, other than that, the enumeration doesn't have a built-in way of
returning the current position within the collection. You'd have to
maintain it yourself somehow.

Pete
 
Peter said:
[...]
In my example, I want to be able to do something on the last loop and
process it (as I do on the previous loops) without having to rewrite
the processing code outside of the loop:

So, if I understand correctly, not only do you want to do something
special when you reach the last element of the loop, the processing of
that last element is supposed to incorporate that "something special".

In that case, yes...there's a benefit to keeping that inside the loop
and tracking a counter may be a better solution. Noting, of course,
that that's mainly because you've said you do have a valid count for
the collection and so it's reasonable to make that test without any
additional work.
Which was why I was asking if that was the best way. Which was to use a
counter - which works fine.
One remaining point: if your processing is some significant amount of
code (more than a couple of lines, for example), then I think it
would be almost as reasonable to put the processing into a separate
method and then call it from two places: inside the loop, and then at
the end.
I say "almost", because doing that actually makes the loop more
awkward. It'd have to look something like this:

Racecar racecarPrev = null;

foreach (Racecar racecar in RaceCarCollection)
{
if (racecarPrev != null)
{
ProcessItem(racecarPrev);
}

racecarPrev = racecar;
}

if (racecarPrev != null)
{
// do "something special"
ProcessItem(racecarPrev);
}
Actually, doing it outside would be fine as well, I also agre about the
separate method (which is actually how I do it). But I don't see how that
makes it less awkward.

In my code (with the separate method) I would do something like:

int ktr = 0;
foreach (Racecar racecar in RaceCarCollection)
{
ktr++;
// Do something...........

if(ktr == RaceCarCollection.Count)
{
// Do last row stuff here...........
}
ProcessItem(racecar)
}

In one case, I would put the ProcessItem in 2 places inside of the loop as I
only process when something about the racecar object changes (the car for
example where I may have the car sorted by type - ford, chevy, mecedes,
ferrari etc). The code then would look something like:

int ktr = 0;
string oldCar = null;

foreach (Racecar racecar in RaceCarCollection)
{
ktr++;
if (oldCar is null)
{
// initialize some variables the first time in
oldCar = racecar.Car;
}
// Do something...........
if (oldCar != racecar.Car)
{
// handle processing all the data for this car
ProcessItem(oldCar)
}

// Handle accumulation of data here for the new car

if(ktr == RaceCarCollection.Count)
{
// Do last row stuff here...........

ProcessItem(racecar.Car)
}
}
That way you don't do the "ProcessItem" work until you know whether
the item is in fact the last one or not. Since the processing is
encapsulated in a separate method, I think it's less of a problem to
duplicate that call (I definitely don't like copy-and-pasting code
that does real work, but duplicating a simple call to a method isn't
so bad if it's otherwise beneficial).
I think you may have misunderstood what I was trying to do.

I wasn't trying to process the data only when I have the last item. I just
needed to make sure that the last one gets processed somewhere (either after
the foreach as you mentioned or inside the loop).

Also, unless I am mistaken, your example would not process the 1st item but
would process all the ones after it, including the last one.
Obviously you wouldn't contort your code to work like the above if you
already have a valid count for your collection and can do the "last
item" test inside the loop. But it could be worth keeping the above
arrangement in mind for dealing with collections that don't expose a
count.

That's true.

Thanks,

Tom
 
[...]
Actually, doing it outside would be fine as well, I also agre about the
separate method (which is actually how I do it). But I don't see how
that
makes it less awkward.

Well, I didn't say that putting the code in a separate method makes the
code less awkward. I just think it's less of a problem to duplicate the
"process an item" code if it's like that, because you're only duplicating
a single line, and one that doesn't itself implement any specific behavior.

Even with calling the processing in a method rather than copying and
pasting all of the processing code, the arrangement I suggested is still
awkward. It just doesn't also have the problem of having a large block of
copy-and-pasted code.
[...]
I think you may have misunderstood what I was trying to do.

Perhaps. I think that I did understand the original question as stated
though. And your subject line reinforces that understanding.
I wasn't trying to process the data only when I have the last item. I
just
needed to make sure that the last one gets processed somewhere (either
after
the foreach as you mentioned or inside the loop).

Judging from the responses, I don't think there's a single person who
replied who understood that you actually have intermediate accumulation
results. Your subject line for this thread is very misleading if that's
the case.
Also, unless I am mistaken, your example would not process the 1st item
but
would process all the ones after it, including the last one.

Well, that's a good example of why I called it awkward. It does indeed
process every single item. But because of the awkward way it's written,
that's not immediately obvious to everyone who reads the code (for
example, you).

Code should be immediately obvious in its intent and operation as much as
possible. The fact that the code I posted isn't immediately obvious is
directly connected to why I consider it awkward. I'd only choose to write
the code like that if there was an important, significant advantage to
doing so that outweighed the relative lack of transparency with respect to
what the code actually does.

Pete
 
Peter Morris said:
What occurs to me is that you only want something done once, so why put it
in the loop at all?

More likely, he wants something *not done* for the last item, for example
inserting a comma to separate items. That something may or may not be easy
to undo.
foreach (RaceCar currentRaceCar in RaceCarCollection)
currentRaceCar.Start();

RaceCarCollection[RaceCarCollection.Count - 1].BreakDown();
 
Ben Voigt said:
More likely, he wants something *not done* for the last item, for example
inserting a comma to separate items. That something may or may not be
easy to undo.

Almost.

Actually, as Peter surmised, many have misunderstood the problem ( which I
am sure I didn't state correctly).

What I was looking for was a way to tell when I actually hit the last row -
to make sure it got processed.

I was going through the loop and if something change, such as a new car
style, then I processed what I had accumulated at that point.

Since I don't process until the car type changes, I don't want to process
the current row yet - just what I had accumulated up to that point.

I then now start a new accumulation with the current row.

But if the current row happens to be the last row - I want to process what I
have at that point.

I could either do it after I leave the foreach loop or process it inside the
loop, which was what I wanted to do. So I need to do a check to see if this
was the last loop - if so process it. Or do something special before
processing.

Thanks,

Tom
foreach (RaceCar currentRaceCar in RaceCarCollection)
currentRaceCar.Start();

RaceCarCollection[RaceCarCollection.Count - 1].BreakDown();
 
I no this is an old post but...

My prefered route is:

foreach (var item in someCollection)
{
if (item != someCollection.Last)
{
// nothing happens here on last object
}
}

Requires System.Linq though.
Is there a way to know if you are looking at the last record record of
foreach loop other then setting up a loop counter that you manually
increment?

foreach (Racecar racecar in RaceCarCollection)
{
...

if last row do something?
}

You can tell how many items you have in the collection but is there way to
tell which row you are looking or if this is the last row?

Thanks,

Tom
Not without counying an test against RaceCarCollection.Count (or .Length)

Why not use a for loop instead?
On Friday, February 29, 2008 8:08 PM Jon Skeet [C# MVP] wrote:

There is using "SmartEnumerable" from my (free) MiscUtil library:

http://pobox.com/~skeet/csharp/miscutil/usage/smartenumerable.html

(Note that in order to do that, it's always really a row ahead of where
it reports. Irrelevant in most cases though.)
Won't the following work? I cannot say it is efficient...

if (racecar == RaceCarCollection [RaceCarCollection.count - 1])
{
// do something...
}
On Saturday, March 01, 2008 4:35 AM Peter Morris wrote:
What occurs to me is that you only want something done once, so why put it
in the loop at all?

foreach (RaceCar currentRaceCar in RaceCarCollection)
currentRaceCar.Start();

RaceCarCollection[RaceCarCollection.Count - 1].BreakDown();
On Saturday, March 01, 2008 7:25 AM Jon Skeet [C# MVP] wrote:

The implicit typing is something I don't need to do anything with, but=20
I'll put in the extension method when I get a bit of time - thanks for=20
the reminder :)

--=20
Jon Skeet - <[email protected]>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk
On Saturday, March 01, 2008 9:56 AM Jon Skeet [C# MVP] wrote:

Well, it sort of does. It has an extension method of Count() - but
unless the iterator is actually an IList it's going to retrieve that
count by iterating through the whole collection. Not exactly efficient.


Ah, true.
On Saturday, March 01, 2008 3:43 PM zack wrote:


Not as far as I am aware of. I have done someting like this with:

foreach (int i =3D 0; i < collection.Count; i++)
{
object =3D collection;
...
if (i =3D=3D collection.Count - 1)
do something to the last object
}

On Saturday, March 01, 2008 3:43 PM Jon Skeet [C# MVP] wrote:
That entirely depends on the type of RaceCarCollection. If it is just
an IEnumerable<T>, it does not have a Count property.

Jon
On Monday, March 03, 2008 7:09 PM Ben Voigt [C++ MVP] wrote:
More likely, he wants something *not done* for the last item, for example
inserting a comma to separate items. That something may or may not be easy
to undo.
 
Back
Top