A smart way to write a loop that has special logic for the first item in a collection

Often I have to code a loop, which in a special case should contain the first element in the collection, the code never seems clear how it should be.

With the exception of the C # language redesign, what is the best way to code these loops?

// this is more code to read then I would like for such a common concept // and it is to easy to forget to update "firstItem" foreach (x in yyy) { if (firstItem) { firstItem = false; // other code when first item } // normal processing code } // this code is even harder to understand if (yyy.Length > 0) { //Process first item; for (int i = 1; i < yyy.Length; i++) { // process the other items. } } 
+12
collections c #
Dec 08 '10 at 17:10
source share
12 answers

You can try:

 collection.first(x=> { //... }).rest(x=> { //... }).run(); 

first / rest will look like this:

 FirstPart<T> first<T>(this IEnumerable<T> c, Action<T> a) { return new FirstPart<T>(c, a); } FirstRest rest<T>(this FirstPart<T> fp, Action<T> a) { return new FirstRest(fp.Collection, fp.Action, a); } 

You will need to define the classified FirstPart and FirstRest. FirstRest will need a run method (e.g. Collection, FirstAction and RestAction):

 void run() { bool first = true; foreach (var x in Collection) { if (first) { FirstAction(x); first = false; } else { RestAction(x); } } } 
+5
Dec 08 '10 at 17:30
source share

What about:

 using (var erator = enumerable.GetEnumerator()) { if (erator.MoveNext()) { ProcessFirst(erator.Current); //ProcessOther(erator.Current); // Include if appropriate. while (erator.MoveNext()) ProcessOther(erator.Current); } } 

You can enable this extension if you want:

 public static void Do<T>(this IEnumerable<T> source, Action<T> firstItemAction, Action<T> otherItemAction) { // null-checks omitted using (var erator = source.GetEnumerator()) { if (!erator.MoveNext()) return; firstItemAction(erator.Current); while (erator.MoveNext()) otherItemAction(erator.Current); } } 
+13
Dec 08 2018-10-12
source share

I will be tempted to use a little linq

 using System.Linq; var theCollectionImWorkingOn = ... var firstItem = theCollectionImWorkingOn.First(); firstItem.DoSomeWork(); foreach(var item in theCollectionImWorkingOn.Skip(1)) { item.DoSomeOtherWork(); } 
+5
Dec 08 2018-10-12
source share

I use the method of the variable first all the time, and that seems completely normal to me. If you like it, you can use LINQ First() and Skip(1)

 var firstItem = yyy.First(); // do the whatever on first item foreach (var y in yyy.Skip(1)) { // process the rest of the collection } 
+4
Dec 08 2018-10-12
source share

The way you wrote it is probably the purest way to write it. In the end, there is logic specific to the first element, so you need to somehow imagine it.

+2
Dec 08 2018-10-12
source share

In such cases, I would simply use a for loop as follows:

 for(int i = 0; i < yyy.Count; i++){ if(i == 0){ //special logic here } } 

Using a for loop will also allow you to do something special in other cases, for example, on the last element, on even elements in a sequence, .. etc.

+2
Dec 08 2018-10-12
source share

IMHO the cleanest way: try to avoid special cases for the first element. Of course, this may not work in any situation, but "special cases" may indicate that your program logic is more complex than it should be.

By the way, I would not have encoded

 if (yyy.Length > 0) { for(int i = 1; i <yyy.Length; i++) { // ... } } 

but instead

  for(int i = 1; i <yyy.Length; i++) { // ... } 

(which in itself is a simple example of how to avoid unnecessary handling of a special case.)

+2
Dec 08 '10 at 17:26
source share

Here is a slightly simpler extension method that does the job. This is a combination of KeithS solution and my answer to a related Java question :

 public static void ForEach<T>(this IEnumerable<T> elements, Action<T> firstElementAction, Action<T> standardAction) { var currentAction = firstElementAction; foreach(T element in elements) { currentAction(element); currentAction = standardAction; } } 
+1
Dec 08 '10 at 17:44
source share

Both of these are perfectly acceptable algorithms for handling the first element in different ways, and there really is no other way to do this. If this pattern repeats a lot, you can hide it for overloading ForEach ():

 public static void ForEach<T>(this IEnumerable<T> elements, Action<T> firstElementAction, Action<T> standardAction) { var firstItem = true; foreach(T element in elements) { if(firstItem) { firstItem = false; firstElementAction(element) } else standardAction(element) } } ... //usage yyy.ForEach(t=>(other code when first item), t=>(normal processing code)); 

Linq makes it a little cleaner:

 PerformActionOnFirstElement(yyy.FirstOrDefault()); yyy.Skip(1).ForEach(x=>(normal processing code)); 
0
Dec 08 '10 at 17:25
source share

Until I personally did this, there is another way using enumerations , which facilitates the need for conditional logic. Something like that:

 void Main() { var numbers = Enumerable.Range(1, 5); IEnumerator num = numbers.GetEnumerator(); num.MoveNext(); ProcessFirstItem(num.Current); // First item while(num.MoveNext()) // Iterate rest { Console.WriteLine(num.Current); } } void ProcessFirstItem(object first) { Console.WriteLine("First is: " + first); } 

Output Example:

 First is: 1 2 3 4 5 
0
Dec 08 '10 at 17:29
source share

Another option I came with is

 enum ItemType { First, Last, Normal } list.Foreach(T item, ItemType itemType) => { if (itemType == ItemType.First) { } // rest of code }; 

Writing an extension method remains as an exercise for the reader ... Should two boolean flags "IsFirst" and "IsLast" be used instead of enumerating ItemType, or ItemType - an object with the properties "IsFirst" and "IsLast"?

0
Dec 9 2018-10-09
source share

My decision:

 foreach (var x in yyy.Select((o, i) => new { Object = o, Index = i } ) { if (x.Index == 0) { // First item logic } else { // Rest of items } } 
0
Aug 13 '19 at 10:44
source share



All Articles