Reputation: 920
Is there a fancy way to know you are in the last loop in a List without using counters
List<string> myList = new List<string>() {"are", "we", "there", "yet"};
foreach(string myString in myList) {
// Is there a fancy way to find out here if this is the last time through?
}
Upvotes: 8
Views: 8509
Reputation: 30700
Depends on your definition of "fancy". This might qualify:
if (myList.Count > 0)
{
myList.Take(myList.Count - 1)).ForEach(myString => doSomething(myString));
doSomethingElse(myList.Last());
}
To me, that seems close to what you're looking for. Note that it's not super high-peformance, but it's short and pretty readable, at least to my eyes. You could also write it this way:
if (myList.Count > 0)
{
foreach (string myString in myList.Take(myList.Count - 1))
{
doSomething(myString);
}
doSomethingElse(myList.Last());
}
Here's another alternative, which I'd consider the most obvious, but it's probably the fastest way of doing this:
if (myList.Count > 0)
{
for (int i = 0; i < myList.Count - 1; i++)
{
doSomething(myList[i]);
}
doSomethingElse(myList.Count - 1);
}
Upvotes: 0
Reputation: 74317
The "fancy" way is to maintain 1 element of lookahead, but why on earth would you want to? Jus maintain a count. Here's the fancy way. No counters, no checking the Count property. All it uses is an enumerator:
using System;
using System.Collections.Generic;
namespace Sandbox
{
class Program
{
enum ListPosition : byte
{
First = 0x01 ,
Only = First|Last ,
Middle = 0x02 ,
Last = 0x04 ,
Exhausted = 0x00 ,
}
private static void WalkList( List<int> numbers )
{
List<int>.Enumerator numberWalker = numbers.GetEnumerator();
bool currFetched = numberWalker.MoveNext();
int currValue = currFetched ? numberWalker.Current : default( int );
bool nextFetched = numberWalker.MoveNext();
int nextValue = nextFetched ? numberWalker.Current : default( int );
ListPosition position ;
if ( currFetched && nextFetched ) position = ListPosition.First ;
else if ( currFetched && ! nextFetched ) position = ListPosition.Only ;
else if ( ! currFetched ) position = ListPosition.Exhausted ;
else throw new InvalidOperationException( "Reached Unreachable Code. Hmmm...that doesn't seem quite right" );
while ( position != ListPosition.Exhausted )
{
string article = ( position==ListPosition.Middle?"a":"the" );
Console.WriteLine( " {0} is {1} {2} item in the list" , currValue , article , position );
currFetched = nextFetched ;
currValue = nextValue ;
nextFetched = numberWalker.MoveNext() ;
nextValue = nextFetched?numberWalker.Current:default( int ) ;
if ( currFetched && nextFetched ) position = ListPosition.Middle ;
else if ( currFetched && ! nextFetched ) position = ListPosition.Last ;
else if ( ! currFetched ) position = ListPosition.Exhausted ;
else throw new InvalidOperationException( "Reached Unreachable Code. Hmmm...that doesn't seem quite right" );
}
Console.WriteLine() ;
return ;
}
static void Main( string[] args )
{
List<int> list1 = new List<int>( new []{ 1 , } ) ;
List<int> list2 = new List<int>( new []{ 1 , 2 , } ) ;
List<int> list3 = new List<int>( new []{ 1 , 2 , 3 , } ) ;
List<int> list4 = new List<int>( new []{ 1 , 2 , 3 , 4 , } ) ;
Console.WriteLine( "List 1:" ) ; WalkList( list1 ) ;
Console.WriteLine( "List 2:" ) ; WalkList( list2 ) ;
Console.WriteLine( "List 3:" ) ; WalkList( list3 ) ;
Console.WriteLine( "List 4:" ) ; WalkList( list4 ) ;
return ;
}
}
}
Upvotes: 0
Reputation: 27864
There are two ways I would do it.
First, with a for
loop instead of foreach
for(int i = 0; i < myList.Count; i++)
{
string myString = myList[i];
bool isLast = i == myList.Count - 1;
...
}
Or, if this needs to work with enumerators, change the order of things. Normally MoveNext
is done as the control of the while loop, but if we do it right at the beginning of the loop, we can use its return to determine if we're at the end of the list or not.
IEnumerator<string> enumerator = myList.GetEnumerator();
bool isLast = !enumerator.MoveNext();
while(!isLast)
{
string myString = enumerator.Current;
isLast = !enumerator.MoveNext();
...
}
Upvotes: 4
Reputation: 505
Well...there is no way to know that. Not as easy as you might guess. The list data structure is just a way to order items one after another. But you don't have a way to say if a item is the last item just using foreach structure. Best use for structure with the count property to know if you hit the last (or previous) item.
var length = list.Count;
for (var idx = 0; idx < list.Count; idx++)
{
if (idx == (length - 1))
{
// The last item. Do something
}
}
hope it helps.
Upvotes: 1
Reputation: 41328
How about using a for loop instead?
List<string> myList = new List<string>() {"are", "we", "there", "yet"};
for (var i=0; i<myList.Count; i++)
{
var myString = myList[i];
if (i==myList.Count-1)
{
// this is the last item in the list
}
}
foreach
is useful - but if you need to keep count of what you are doing, or index things by count, then just revert to a good old for
loop.
Upvotes: 4
Reputation: 19057
No, you will have to use a regular for(int i=0; i<myList.Length; i++) { ... }
loop for that.
Upvotes: 10
Reputation: 67283
There's no efficient way to do this.
Just use a for
loop and index. It runs faster any way.
Upvotes: 1
Reputation: 61467
No, there isn't, you'll have to use a counter if you want to use foreach
.
Upvotes: 0