tags:

views:

169

answers:

5

I learned the intersperse function from Haskell, and have been looking for an implementation in c#.

Intersperse takes 2 arguments, an IEnumerable<T> source and a T element. It returns an IEnumerable with element inserted between every element of source.

One possible use-case is to put an arbitrary interger in between a list of integers, for example:

// returns: {1, 0, 2, 0, 3}
(List<int>() {1, 2, 3}).Intersperse(0);

This is a general case of string.Join(...).

A: 

Make it yourself?

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T value)
{
    foreach (var item in source)
    {
        yield return item;
        yield return value;
    }
}
Daniel Pratt
That has one to many "value"s
Marc Gravell
A: 

If you're wondering how to implement it, I would do so like this:

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> collection, T value)
{
    foreach(T item in collection)
    {
        yield return item;
        yield return value;
    }

    yield break;
}
Andy
That has one to many "value"s
Marc Gravell
+3  A: 

Something the others have missed: if you only want it in between items, and not also in front or behind, you need to do an extra check:

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element)
{
    bool first = true;
    foreach (T value in source)
    {
        if (!first) yield return element;
        yield return value;
        first = false;
    }
}
Joel Coehoorn
Ah! beat me to it!
Daniel
Indeed, seconds in it...
Marc Gravell
Your point makes sense, but I'm confused by your answer. It seems in your example that the interspersed item will come first, which I don't think is right.
Daniel Pratt
I like yours better anyway, Marc. It doesn't need a negation for the if block on only sets the flag once.
Joel Coehoorn
@Daniel: The first time through the loop it's not output at all, so a the first enumerated value is returned ahead of the first interspersed item.
Joel Coehoorn
@Daniel Pratt - no, the code looks fine to me. Joel's used the opposite meaning of "item" and "value" to me, but the code is right.
Marc Gravell
Ah, gotcha. Those pesky ! are so easy to miss.
Daniel Pratt
Updated my post with some less confusing names.
Joel Coehoorn
Is taking the conditional out of the loop any better, as in my solution?
Daniel
6 of 1... I like this better because it avoid repeating the "yield return i.Current;", and it's shorter. But it's probably just a matter of preference.
Joel Coehoorn
I'm choosing this answer because it was the first correct implementation. You all rock! This got answers faster than I could say "caramba!".
Daniel
+1  A: 

I've coded up a solution that is lazy, in the spirit of Linq solutions! Other solutions I came up with involved traversing the entire list before returning data, and then returning the resulting list.

Some of the other answers have an if check on every iteration of the loop.

public static class Helper {
   public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T element) {
      using(var i = source.GetEnumerator()) {
         if (i.MoveNext()) yield return i.Current;
         while(i.MoveNext()) {
            yield return element;
            yield return i.Current;
         }
      }
   }
}
Daniel
When using GetEnumerator(), you should Dispose() the iterator
Marc Gravell
@Marc, thanks for pointing that out!
Daniel
+1  A: 

It would be pretty easy to write:

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> source, T value) {
    bool first = true;
    foreach(T item in source) {
         if(first) { first = false; }
         else { yield return value; }
         yield return item;
    }
}
Marc Gravell