Reputation: 54127
When foreach
ing 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.
Upvotes: 19
Views: 32255
Reputation: 4994
Select()
can return the index of the current element. With a simple extension method, you can do this:
var ints = new List<int> { 1, 2, 3, 4 };
foreach (var item in ints.SelectWithIndex())
{
if (item.index == 0)
Console.WriteLine($"First: {item.val}");
else
Console.WriteLine($"{item.val}");
}
The extension method SelectWithIndex
adds an index to each value of a list (it returns a list of tuples having to fields: val
and index
).
static class MyExtensions
{
public static IEnumerable<(T val, int index)> SelectWithIndex<T>(this List<T> list)
{
return list.Select((i, j) => CreateValIndex(i, j));
}
public static (T val, int index) CreateValIndex<T>(T val, int index)
{
return (val, index);
}
}
Upvotes: 0
Reputation: 1
Editing way from another answer:
IList<object> objs = new List<object>
{
new Object(),
new Object(),
new Object(),
new Object()
};
// to access first element: objs[0]
// to access last element: objs[objs.Count - 1]
System.Diagnostics.Debug.WriteLine("First object - do something special");
foreach (object o in objs.Skip(1))
{
System.Diagnostics.Debug.WriteLine("object Do something else");
}
Reference link about:
Using Skip
: Enumerable.Skip Method (IEnumerable, Int32)
Using IList<T>
: List or IList
Upvotes: 0
Reputation: 1063559
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).
Upvotes: 29
Reputation: 57872
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
}
Upvotes: 3
Reputation: 1502406
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.
Upvotes: 10
Reputation: 26599
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 :)
Upvotes: 0
Reputation: 24433
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.
Upvotes: 1
Reputation: 46410
Instead of if (i == ints.First())
, does it work if you use if (i.Equals(ints.First())
?
Upvotes: 0
Reputation: 110171
foreach(int i in ints.Take(1))
{
//do first thing
}
foreach(int i in ints.Skip(1))
{
//do other things
}
Upvotes: 7