tags:

views:

1360

answers:

7

When foreaching through a generic list I often want to do something different for the first element in the list:

List<object> objs = new List<object>
{
    new Object(),
    new Object(),
    new Object(),
    new Object()
};

foreach (object o in objs)
{
    if (o == objs.First())
    {
        System.Diagnostics.Debug.WriteLine("First object - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("object Do something else");
    }
}

This will output:

    First object - do something special
    object Do something else
    object Do something else
    object Do something else

This is all fine and dandy.

However if my generic list is of a value type, this approach will fail.

List<int> ints = new List<int> { 0, 0, 0, 0 };
foreach (int i in ints)
{
    if (i == ints.First())
    {
        System.Diagnostics.Debug.WriteLine("First int - do something special");
    }
    else
    {
        System.Diagnostics.Debug.WriteLine("int Do something else");
    }
}

This will output:

    First int - do something special
    First int - do something special
    First int - do something special
    First int - do something special

Now I know I could recode this to add a boolean flag variable or traditional for loop, but I am wondering if there's any way to find out if a foreach loop is on the first iteration of its looping.

+6  A: 

Well, you could code it using explicit iteration:

using(var iter = ints.GetEnumerator()) {
  if(iter.MoveNext()) {
     // do "first" with iter.Current

     while(iter.MoveNext()) {
       // do something with the rest of the data with iter.Current
     }
  }
}

The bool flag option (with foreach) is probably easier though... that is what I (almost) always do!

Another option would be LINQ:

if(ints.Any()) {
  var first = ints.First();
  // do something with first
}

foreach(var item in ints.Skip(1)) {
  // do something with the rest of them
}

The downside of the above is that it tries to look at the list 3 times... since we know it is a list, that is fine - but if all we had was an IEnumerable<T>, it would only be sensible to iterate it once (since the source might not be re-readable).

Marc Gravell
+2  A: 
foreach(int i in ints.Take(1))
{
 //do first thing
}

foreach(int i in ints.Skip(1))
{
  //do other things
}
David B
A: 

Instead of if (i == ints.First()), does it work if you use if (i.Equals(ints.First())?

Jeremy
No, the point is that all the elements are the same - every element is equal to the first element. The same would happen with a reference type if the list contained the same reference twice.
Jon Skeet
I suspect it'd work out to the same thing, since the equality of value types is determined by their value, not referentially.
GWLlosa
Ninja'd by Jon Skeet with a better answer.
GWLlosa
+1  A: 

The issue you're having is because equality for value types is based on the actual value, not the reference itself. Specifically, in your example, your list is all zeroes, so its asking if currentItem == firstItem, and since they're both zero, this is always true. For this kind of thing, I've always wound up using a boolean flag.

GWLlosa
A: 

If you really don't want to use a boolean, you could do something like:

List<int> ints = new List<int> { 0, 0, 0, 0 };

System.Diagnostics.Debug.WriteLine("First int - do something special" + ints.FirstOrDefault().ToString());
foreach (int i in ints.Skip(1))
{
    {
        System.Diagnostics.Debug.WriteLine("int Do something else");
    }
}

But that seems a bit.. wierd :)

Steven Robbins
+2  A: 

A while ago I wrote SmartEnumerable (part of MiscUtil) which lets you know if the current element is the first or last, as well as its index. That may help you... it's part of MiscUtil, which is open source - you can take just SmartEnumerable under the same licence, of course.

Sample code (c'n'p from the web page):

using System;
using System.Collections.Generic;

using MiscUtil.Collections;

class Example
{
    static void Main(string[] args)
    {
        List<string> list = new List<string>();
        list.Add("a");
        list.Add("b");
        list.Add("c");
        list.Add("d");
        list.Add("e");

        foreach (SmartEnumerable<string>.Entry entry in
                 new SmartEnumerable<string>(list))
        {
            Console.WriteLine ("{0,-7} {1} ({2}) {3}",
                               entry.IsLast  ? "Last ->" : "",
                               entry.Value,
                               entry.Index,
                               entry.IsFirst ? "<- First" : "");
        }
    }
}

EDIT: Note that while it works with reference types with distinct references, it'll still fail if you give it a list where the first reference crops up elsewhere in the list.

Jon Skeet
A perfect "var" candidate if ever I saw one ;-p
Marc Gravell
Also calls out for a ForEach overload that takes an Action<int, bool, T>, or an Action<FooState<T>> - i.e. list.ForEach((index, isLast, value) => {...});
Marc Gravell
var: Absolutely. Written before C# 3 of course :) And agreed on the Action<int, bool, T> or at least Action<int, T>. More for the More LINQ project...
Jon Skeet
Yes... the slightly ungainly bool usage is why I didn't include "isFirst" (let "index" cover that)... how valuable it is to know if you are on the *last* item is an interesting question (I was simply trying to cover all the options available in your sample above).
Marc Gravell
+1  A: 

Since you're already using LINQ, you could do this:

var list = new List<Int32>();

// do something with list.First();

foreach (var item in list.Skip(1))
{
    // do other stuff
}
Daniel Schaffer
Doh, Mark beat me to it with his edit :\
Daniel Schaffer