Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

Last row in foreach loop

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
tshad - 29 Feb 2008 21:02 GMT
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
Bjørn Brox - 29 Feb 2008 21:16 GMT
tshad skrev:
> 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
[quoted text clipped - 9 lines]
> 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?

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

Why not use a for loop instead?

Signature

Bjørn Brox

tshad - 02 Mar 2008 00:43 GMT
> tshad skrev:
>> Is there a way to know if you are looking at the last record record of
[quoted text clipped - 14 lines]
>
> Why not use a for loop instead?

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.

Thanks,

Tom
Bjørn Brox - 02 Mar 2008 14:21 GMT
tshad skrev:
>> tshad skrev:
>>> Is there a way to know if you are looking at the last record record of
[quoted text clipped - 24 lines]
> 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?

Signature

Bjørn Brox

tshad - 02 Mar 2008 17:38 GMT
> tshad skrev:
>>> tshad skrev:
[quoted text clipped - 29 lines]
> 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 - 02 Mar 2008 20:39 GMT
> [...]
>>> I was just curious if there was a property somewhere that told you what
[quoted text clipped - 6 lines]
>
> 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
zacks@construction-imaging.com - 29 Feb 2008 21:17 GMT
> 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
[quoted text clipped - 14 lines]
>
> Tom

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

foreach (int i = 0; i < collection.Count; i++)
{
   object = collection[i];
   ...
   if (i == collection.Count - 1)
       do something to the last object
}
zacks@construction-imaging.com - 29 Feb 2008 21:44 GMT
On Feb 29, 4:17 pm, za...@construction-imaging.com wrote:

> > 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
[quoted text clipped - 25 lines]
>
> }

Sorry, that should be for instead of foreach.
tshad - 02 Mar 2008 00:47 GMT
On Feb 29, 4:02 pm, "tshad" <ts...@dslextreme.com> wrote:
> 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
[quoted text clipped - 16 lines]
>
>Not as far as I am aware of. I have done someting like this with:

>foreach (int i = 0; i < collection.Count; i++)
>{
[quoted text clipped - 3 lines]
>        do something to the last object
>}

Something like this is fine but I might just as well do:

int iKtr = 0;
foreach (Racecar racecar in RaceCarCollection)
{
       iKtr++;
       ...

       if( iKtr == RaceCarCollection.Count)
            do something?
}

Thanks,

Tom
Peter Bromberg [C# MVP] - 29 Feb 2008 21:21 GMT
Doubt it. I've always use a counter variable and compared to the length of
the collection. Not a big deal.
-- Peter
Site: http://www.eggheadcafe.com
UnBlog: http://petesbloggerama.blogspot.com
Short Urls & more: http://ittyurl.net

> 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
[quoted text clipped - 13 lines]
>
> Tom
tshad - 02 Mar 2008 00:48 GMT
> Doubt it. I've always use a counter variable and compared to the length of
> the collection. Not a big deal.

I do the same.  And as you said, no big deal.

I was just curious if there was a better way.

Thanks,

Tom
> -- Peter
> Site: http://www.eggheadcafe.com
[quoted text clipped - 19 lines]
>>
>> Tom
Rene - 29 Feb 2008 21:27 GMT
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...........
}

> 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
[quoted text clipped - 13 lines]
>
> Tom
tshad - 02 Mar 2008 00:50 GMT
> 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
[quoted text clipped - 11 lines]
>    // Do last row stuff here...........
> }

Huh?

How would racecar ever be null?

lastRaceCar would always be equal to the current row.

Tom

>> 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
[quoted text clipped - 13 lines]
>>
>> Tom
Peter Duniho - 02 Mar 2008 01:08 GMT
>> Racecar lastRacecar;
>> foreach (Racecar racecar in RaceCarCollection)
[quoted text clipped - 11 lines]
>
> 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
tshad - 02 Mar 2008 08:19 GMT
>> Racecar lastRacecar;
>> foreach (Racecar racecar in RaceCarCollection)
[quoted text clipped - 15 lines]
>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.

>> lastRaceCar would always be equal to the current row.
>
>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

>Pete
Peter Duniho - 02 Mar 2008 10:56 GMT
> [...]
>> If you fix the compiler error and assign null to the local variable  
[quoted text clipped - 8 lines]
> 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 - 02 Mar 2008 17:52 GMT
>> [...]
>>> If you fix the compiler error and assign null to the local variable
[quoted text clipped - 15 lines]
> 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
> Pete
Peter Duniho - 02 Mar 2008 19:45 GMT
> [...]
> 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
tshad - 02 Mar 2008 22:03 GMT
>> [...]
>> In my example, I want to be able to do something on the last loop and
[quoted text clipped - 10 lines]
> 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
[quoted text clipped - 21 lines]
>          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

> Pete
Peter Duniho - 02 Mar 2008 23:27 GMT
> [...]
> 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
christery@gmail.com - 29 Feb 2008 21:32 GMT
> 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
[quoted text clipped - 14 lines]
>
> Tom

Sounds like you should test doing that with a database, the price for
MySQL or if you want to go M$ SQLserverExpress is nice (0$), a
pseudostatement like "readline(); if next line is EOF..." is not too
hard to program logically but you get double the IO, and for
nothing..  or as mentioned check the length..
//CY
Darren - 29 Feb 2008 21:48 GMT
You can get the enumerator then use Reset, Current, & MoveNext but it's ugly.

IEnumerator<RaceCar> e = RaceCarCollection.GetEnumerator();
e.Reset();
bool isValid = e.MoveNext();
while (isValid)
{
    RaceCar car = e.Current;
    isValid = e.MoveNext();
    if ( !isValid )
    {
        // this is the last car
    }
}

> 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
[quoted text clipped - 13 lines]
>
> Tom
Jon Skeet [C# MVP] - 01 Mar 2008 01:08 GMT
> 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?

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.)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Søren Reinke - 01 Mar 2008 09:20 GMT
>> 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
[quoted text clipped - 3 lines]
>
> http://pobox.com/~skeet/csharp/miscutil/usage/smartenumerable.html

Nice :)
'When C# 3 is released, this pattern will be made even simpler with'

Any chance for that update ? :-)

See ya
Søren Reinke
Jon Skeet [C# MVP] - 01 Mar 2008 12:25 GMT
> > http://pobox.com/~skeet/csharp/miscutil/usage/smartenumerable.html
>
> Nice :)
> 'When C# 3 is released, this pattern will be made even simpler with'
>
> Any chance for that update ? :-)

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

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Søren Reinke - 01 Mar 2008 13:29 GMT
>>> http://pobox.com/~skeet/csharp/miscutil/usage/smartenumerable.html
>> Nice :)
[quoted text clipped - 5 lines]
> I'll put in the extension method when I get a bit of time - thanks for
> the reminder :)

:-)

No problem.

I just enjoy getting tips from your website :-)

See ya
Søren Reinke
Family Tree Mike - 01 Mar 2008 01:50 GMT
> 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
[quoted text clipped - 13 lines]
>
> Tom

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

if (racecar == RaceCarCollection [RaceCarCollection.count - 1])
{
 // do something...
}
Jon Skeet [C# MVP] - 01 Mar 2008 03:50 GMT
On Mar 1, 1:50 am, Family Tree Mike
<FamilyTreeM...@discussions.microsoft.com> wrote:
> Won't the following work?  I can't say it's efficient...
>
[quoted text clipped - 3 lines]
>
> }

That entirely depends on the type of RaceCarCollection. If it's just
an IEnumerable<T>, it doesn't have a Count property.

Jon
Family Tree Mike - 01 Mar 2008 13:44 GMT
> On Mar 1, 1:50 am, Family Tree Mike
> <FamilyTreeM...@discussions.microsoft.com> wrote:
[quoted text clipped - 10 lines]
>
> Jon

I see your point in earlier versions, but under 2008, IEnumerable<T>
supports a Count() method, but not a property.  The original poster said they
knew the count though.
Jon Skeet [C# MVP] - 01 Mar 2008 14:56 GMT
> I see your point in earlier versions, but under 2008, IEnumerable<T>
> supports a Count() method, but not a property.

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.

> The original poster said they knew the count though.

Ah, true.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

tshad - 02 Mar 2008 00:53 GMT
>> 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
[quoted text clipped - 21 lines]
>  // do something...
> }

Actually, that might work.

Thanks,

Tom
Peter Morris - 01 Mar 2008 09:35 GMT
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();
tshad - 02 Mar 2008 00:56 GMT
> What occurs to me is that you only want something done once, so why put it
> in the loop at all?
[quoted text clipped - 3 lines]
>
> RaceCarCollection[RaceCarCollection.Count - 1].BreakDown();

What does BreakDown do?

Thanks,

Tom
Ben Voigt [C++ MVP] - 04 Mar 2008 00:09 GMT
> 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();
tshad - 04 Mar 2008 01:20 GMT
>> 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.

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();

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.