2

I have a function running over an enumerable, but the function should be a little bit different for the first item, for example:

void start() { 
    List<string> a = ...
    a.ForEach(DoWork);
}

bool isFirst = true;

private void DoWork(string s) {
   // do something

   if(isFirst)
     isFirst = false;
   else
     print("first stuff");

   // do something
}

How would you refactor this to avoid that ugly flag?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
onof
  • 17,167
  • 7
  • 49
  • 85

8 Answers8

9

Expounding on Jimmy Hoffa's answer if you actually want to do something with the first item you could do this.

DoFirstWork(a[0])

a.Skip(1).ForEach(DoWork)

If the point is that it is separate in logic from the rest of the list then you should use a separate function.

msarchet
  • 15,104
  • 2
  • 43
  • 66
  • +1 i like this solution much more than the custom enumerator class. The case is too specific, and i prefer the `handle first item differently design pattern`, which you have here. The custom enumerator class will next need to treat the last one differently. Then it's a class to treat the first and last differently. Then we'll need an enumeration factory with custom expressions that allow **first**, **even**, **odd**, **first even**, **first odd**, **last**, **last even**, **last odd** to be handled specially. i think sticking with the design pattern is best. – Ian Boyd Aug 31 '10 at 15:14
2

It might be a bit heavy handed, but I pulled this from another SO question a while back.

public static void IterateWithSpecialFirst<T>(this IEnumerable<T> source,
    Action<T> firstAction,
    Action<T> subsequentActions)
{
    using (IEnumerator<T> iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
        {
            firstAction(iterator.Current);
        }
        while (iterator.MoveNext())
        {
            subsequentActions(iterator.Current);
        }
    }
}
Andy_Vulhop
  • 4,699
  • 3
  • 25
  • 34
1

Check out Jon Skeet's smart enumerations.

They are part of his Miscellaneous Utility Library

Rune
  • 8,340
  • 3
  • 34
  • 47
0
using System.Linq; // reference to System.Core.dll

List<string> list = ..
list.Skip(1).ForEach(DoWork) // if you use List<T>.ForEeach()

but I recommend you to write your one:

public static void ForEach(this IEnumerable<T> collection, Action<T> action)
{
    foreach(T item in collection)
        action(item);
}

So you could do just next:

list.Skip(1).ForEach(DoWork)
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • 1
    ForEach isn't his extension method, it is an extension method built in which takes List but for god knows why, the linq folks didn't give us a default implementation for IEnumerable.. – Jimmy Hoffa Aug 31 '10 at 14:34
  • 1
    @abatishchev, @Jimmy: It [isn't an extension method at all](http://msdn.microsoft.com/en-us/library/bwabdf9z.aspx). Also see [Eric Lippert's blog post](http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx) about the reasoning behind the decision to leave it out of the LINQ extension methods. – Dan Tao Aug 31 '10 at 14:39
  • 2
    @Jimmy Hoffa: I think the reason there's no ForEach method on IEnumerable is because you can't assume you are allowed to change the items. Something similar would be using the Select() extension method, but that would result in a copy of your list. – Erik van Brakel Aug 31 '10 at 14:41
  • @Dan: Yea, I know http://stackoverflow.com/questions/101265/why-is-there-not-a-foreach-extension-method-on-the-ienumerable-interface – abatishchev Aug 31 '10 at 14:42
  • @Erik van Brakel: That makes sense, thanks for illuminating this for me. Always drives me nuts. – Jimmy Hoffa Aug 31 '10 at 14:42
  • @Erik, @Dan: imo reason described by Eric is stupid. Something is being covered by philosophy – abatishchev Aug 31 '10 at 14:49
  • @abatischev: Well all right, then... If you think it's stupid, I guess there's not much more to say about it! – Dan Tao Aug 31 '10 at 15:39
0

It's hard to say what the "best" way to handle the first element differently is without knowing why it needs to be handled differently.

If you're feeding the elements of the sequence into the framework's ForEach method, you can't elegantly provide the Action delegate the information necessary for it to determine the element parameter's position in the source sequence, so I think an extra step is necessary. If you don't need to do anything with the sequence after you loop through it, you could always use a Queue (or Stack), pass the first element to whatever handler you're using through a Dequeue() (or Pop()) method call, and then you have the leftover "homogeneous" sequence.

Patrick
  • 971
  • 9
  • 17
0

It might seem rudimentary with all the shiny Linq stuff available, but there's always the old fashion for loop.

var yourList = new List<int>{1,1,2,3,5,8,13,21};
for(int i = 0; i < yourList.Count; i++)
{
    if(i == 0)
        DoFirstElementStuff(yourList[i]);
    else
        DoNonFirstElementStuff(yourList[i]);
}

This would be fine if you don't want to alter yourList inside the loop. Else, you'll probably need to use the iterator explicitly. At that point, you have to wonder if that's really worth it just to get rid of an IsFirst flag.

Andy_Vulhop
  • 4,699
  • 3
  • 25
  • 34
0

EDIT: added usage example, added a ForFirst method, reordered my paragraphs.

Below is a complete solution.

Usage is either of the following:

        list.ForFirst(DoWorkForFirst).ForRemainder(DoWork);
        // or 
        list.ForNext(1, DoWorkForFirst).ForRemainder(DoWork);

The crux is the ForNext method, which performs an action for the specified next set of items from the collection and returns the remaining items. I've also implemented a ForFirst method that simply calls ForNext with count: 1.

class Program
{
    static void Main(string[] args)
    {
        List<string> list = new List<string>();
        // ...

        list.ForNext(1, DoWorkForFirst).ForRemainder(DoWork);
    }

    static void DoWorkForFirst(string s)
    {
        // do work for first item
    }

    static void DoWork(string s)
    {
        // do work for remaining items
    }
}

public static class EnumerableExtensions
{
    public static IEnumerable<T> ForFirst<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        return enumerable.ForNext(1, action);
    }

    public static IEnumerable<T> ForNext<T>(this IEnumerable<T> enumerable, int count, Action<T> action)
    {
        if (enumerable == null)
            throw new ArgumentNullException("enumerable");

        using (var enumerator = enumerable.GetEnumerator())
        {
            // perform the action for the first <count> items of the collection
            while (count > 0)
            {
                if (!enumerator.MoveNext())
                    throw new ArgumentOutOfRangeException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Unexpected end of collection reached.  Expected {0} more items in the collection.", count));

                action(enumerator.Current);

                count--;
            }

            // return the remainder of the collection via an iterator
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
        }
    }

    public static void ForRemainder<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        if (enumerable == null)
            throw new ArgumentNullException("enumerable");

        foreach (var item in enumerable)
        {
            action(item);
        }
    }
}

I felt a bit ridiculous making the ForRemainder method; I could swear that I was re-implementing a built-in function with that, but it wasn't coming to mind and I couldn't find an equivalent after glancing around a bit. UPDATE: After reading the other answers, I see there apparently isn't an equivalent built into Linq. I don't feel so bad now.

Dr. Wily's Apprentice
  • 10,212
  • 1
  • 25
  • 27
  • Thank you, I solved using this approach and Decorator pattern: list.ForFirst(new EndWork(new WorkForFirst(new StartWork()).DoWork).ForRemainder(new EndWork(new StartWork()).DoWork); – onof Aug 31 '10 at 19:31
0

Depends on how you're "handling it differently". If you need to do something completely different, then I'd recommend handling the first element outside the loop. If you need to do something in addition to the regular element processing, then consider having a check for the result of the additional processing. It's probably easier to understand in code, so here's some:

string randomState = null; // My alma mater!
foreach(var ele in someEnumerable) {
    if(randomState == null) randomState = setState(ele);
    // handle additional processing here.
}

This way, your "flag" is really an external variable you (presumably) need anyway, so you're not creating a dedicated variable. You can also wrap it in an if/else if you don't want to process the first element like the rest of the enumeration.

TMN
  • 3,060
  • 21
  • 23