7

What would be the most elegant way to process the first IEnumerable item differently than others, without having to test on each iteration?

With a test on each iteration, it would look like this:

// "first item done" flag
bool firstDone = false;

// items is an IEnumerable<something>
foreach (var item in items)
{
    if (!firstDone)
    {
        // do this only once
        ProcessDifferently(item);
        firstDone = true;
        continue;
    }

    ProcessNormally(item);
}

If I do this:

ProcessDifferently(items.First());
ProcessNormally(items.Skip(1)); // this calls `items.GetEnumerator` again

it will invoke GetEnumerator twice, which I would like to avoid (for Linq-to-Sql cases, for example).

How would you do it, if you need to do several times around your code?

dilbert
  • 73
  • 3

3 Answers3

12

If I needed to do it in several places, I'd extract a method:

public void Process<T>(IEnumerable<T> source,
                       Action<T> firstAction,
                       Action<T> remainderAction)
{
    // TODO: Argument validation
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
        {
            firstAction(iterator.Current);
        }
        while (iterator.MoveNext())
        {
            remainderAction(iterator.Current);
        }
    }
}

Called as:

Process(items, ProcessDifferently, ProcessNormally);

There are other options too, but it would really depend on the situation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

Here's another way:

    private static void Main(string[] args)
    {
        var testdata = new[] { "a", "b", "c", "d", "e" };

        var action = FirstThenRest<string>(
            s => Console.WriteLine("First: " + s),
            s => Console.WriteLine("Rest: " + s));

        foreach (var s in testdata)
            action(s);
    }

    public static Action<T> FirstThenRest<T>(Action<T> first, Action<T> rest)
    {
        Action<T> closure = t =>
                            {
                                first(t);
                                closure = rest;
                            };

        return t => closure(t);
    }

This outputs:

First: a
Rest: b
Rest: c
Rest: d
Rest: e

No conditionals. :D

EDIT: "Head" and "Tail" would probably be better terms but I'm too lazy to go change it now.

Jim Bolla
  • 8,265
  • 36
  • 54
0

You can do it the old fashioned way:

var itemsList = items.ToList();
ProcessDifferently(itemsList[0]);

for(int i=1;i<itemsList.Count;i++)
{
   ProcessNormally(itemsList[i]);
}
HitLikeAHammer
  • 2,679
  • 3
  • 37
  • 53