Is there any way to know that I am getting the last element in a foreach loop

I need to make a special treatment for the last item in the collection. I am wondering if I can know that I hit the last element when using the foreach loop.

+5
source share
7 answers

The only way I know is to increase the counter and compare with the length when exiting, or when exiting the loop, set the Boolean flag loopExitedEarly.

+5
source

There is no direct way. You will need to save the buffering of the next item.

IEnumerable<Foo> foos = ...

Foo prevFoo = default(Foo);
bool elementSeen = false;

foreach (Foo foo in foos)
{
    if (elementSeen) // If prevFoo is not the last item...
        ProcessNormalItem(prevFoo);

    elementSeen = true;
    prevFoo = foo;
}

if (elementSeen) // Required because foos might be empty.
    ProcessLastItem(prevFoo);

Alternatively, you can use the desired enumerator to do the same:

using (var erator = foos.GetEnumerator())
{
    if (!erator.MoveNext())
        return;

    Foo current = erator.Current;

    while (erator.MoveNext())
    {
        ProcessNormalItem(current);
        current = erator.Current;
    }

    ProcessLastItem(current);
}

, , ( Count ICollection ICollection<T>) - (, , for-loop ):

int numItemsSeen = 0;

foreach(Foo foo in foos)
{
   if(++numItemsSeen == foos.Count)
       ProcessLastItem(foo)

   else ProcessNormalItem(foo);
}

MoreLinq, :

foreach (var entry in foos.AsSmartEnumerable()
{
    if(entry.IsLast)
       ProcessLastItem(entry.Value)

    else ProcessNormalItem(entry.Value);
}

, :

Foo[] fooArray = foos.ToArray();

foreach(Foo foo in fooArray.Take(fooArray.Length - 1))
    ProcessNormalItem(foo);

ProcessLastItem(fooArray.Last());
+4

, , for, :

string[] names = { "John", "Mary", "Stephanie", "David" };
int iLast = names.Length - 1;
for (int i = 0; i <= iLast; i++) {
    Debug.Write(names[i]);
    Debug.Write(i < iLast ? ", " : Environment.NewLine);
}

, String.Join:).


, , , :

void Enumerate<T>(IEnumerable<T> items, Action<T, bool> action) {
    IEnumerator<T> enumerator = items.GetEnumerator();
    if (!enumerator.MoveNext()) return;
    bool foundNext;
    do {
        T item = enumerator.Current;
        foundNext = enumerator.MoveNext();
        action(item, !foundNext);
    }
    while (foundNext);
}

...

string[] names = { "John", "Mary", "Stephanie", "David" };
Enumerate(names, (name, isLast) => {
    Debug.Write(name);
    Debug.Write(!isLast ? ", " : Environment.NewLine);
})
+1

(. ). ( - #):

  IEnumerator<string> it = foo.GetEnumerator();
  for (bool hasNext = it.MoveNext(); hasNext; ) {
     string element = it.Current;
     hasNext = it.MoveNext();

     if (hasNext) { // normal processing
        Console.Out.WriteLine(element);
     } else { // special case processing for last element
        Console.Out.WriteLine("Last but not least, " + element);
     }
  }

, : Mitch , . J.D. , . . ( ) - , .

, Enumerator , . K.I.S.S.

Java, hasNext, MoveNext. IEnumerable, Java, , .

+1

, . , .

// functor class
class Runner {
    string ArgString {get;set;}
    object ArgContext {get;set;}

    // CTOR: encapsulate args and a context to run them in
    public Runner(string str, object context) {
        ArgString = str;
        ArgContext = context;
    }

    // This is the item processor logic. 
    public void Process() {
        // process ArgString normally in ArgContext
    }
}

foreach :

// intended to track previous item in the loop
var recent = default(Runner); // see Runner class above

// normal foreach iteration
foreach(var str in listStrings) {
    // is deferred because this executes recent item instead of current item
    if (recent != null)
        recent.Process(); // run recent processing (from previous iteration)

    // store the current item for next iteration
    recent = new Runner(str, context);
}

// now the final item remains unprocessed - you have a choice
if (want_to_process_normally)
    recent.Process(); // just like the others
else
    do_something_else_with(recent.ArgString, recent.ArgContext);

- , . .

Shorter workaround

If you want to apply special processing to the last element after they are regularly processed ...

// example using strings
var recentStr = default(string);

foreach(var str in listStrings) {
    recentStr = str;
    // process str normally
}

// now apply additional special processing to recentStr (last)

This is a potential workaround.

0
source
List<int> numbers = new ....;
int last = numbers.Last();

Stack<int> stack = new ...;
stack.Peek();

Update

    var numbers = new int[] { 1, 2,3,4,5 };

    var enumerator = numbers.GetEnumerator();
    object last = null;
    bool hasElement = true;

    do
    {
        hasElement = enumerator.MoveNext();

        if (hasElement)
        {
            last = enumerator.Current;
            Console.WriteLine(enumerator.Current);
        }
        else
            Console.WriteLine("Last = {0}", last);

    } while (hasElement);

    Console.ReadKey();
0
source

This special processing can only be done during processing in the foreach loop. Could this be done when added to the collection. If this is your case, use your own collection,

 public class ListCollection : List<string>
    {
        string _lastitem;

        public void Add(string item)
        {
            //TODO: Do  special treatment on the new Item, new item should be last one.
            //Not applicable for filter/sort
             base.Add(item);
        }
    }
0
source

All Articles