.NET Forum / Languages / C# / March 2008
Last row in foreach loop
|
|
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 MagazinesGet 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 ...
|
|
|