Tuesday, April 24, 2012

A Useful Linq Extension Method: Intersperse

Have you ever had a case where you want to insert a constant item in-between each element of a list of items? For example, if I’ve got a list of numbers:

1,2,3,4,5

And I want to insert 0 between each one to get:

1,0,2,0,3,0,4,0,5

Meet my friend Intersperse (stolen from Haskell, if you hadn’t guessed):

var items = new[] {1, 2, 3, 4, 5}.Intersperse(0).ToArray();

foreach (var item in items)
{
Console.Write(item);
}

This code outputs:

102030405

And here is the Intersperse extension method:

public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> items, T separator)
{
var first = true;
foreach (var item in items)
{
if (first) first = false;
else
{
yield return separator;
}
yield return item;
}
}

Some interesting uses. System.String is, of course, an array of char, so you can do this:

var doubleSpaced = new string("hello world".Intersperse(' ').ToArray());
Console.WriteLine(doubleSpaced);
// outputs 'h e l l o w o r l d'

Or how about this perennial problem, creating a comma separated list:

var strings = new[] {"one", "two", "three"};
var commaSeprated = new string(strings.Intersperse(",").Concat().ToArray());
Console.WriteLine(commaSeprated);
// outputs one,two,three

7 comments:

  1. I use string.Join() for the last example, but Intersperse has more usages. I like it.

    ReplyDelete
  2. Cool stuff.

    If you use IEnumerator move next instead of foreach you would keep the evaluation lazy.

    ReplyDelete
  3. Hi oskark, I was just about to write a reply saying, nope, foreach creates a lazy evaluation, when I thought that I'd better try it first. By jove, you're right! I was surprised. When I've got a minute I'll try and re-write it with your suggestion. Many thanks.

    ReplyDelete
  4. Simon3:54 pm

    Lazy in which sense? The compiler transforms the foreach in a call to GetEnumerator, so I fail to see how foreach could be "less lazy" than GetEnumerator.

    ReplyDelete
  5. Larry Smith4:30 pm

    .Net 4.0 has the Zip extension method that will let you do this.

    For several examples, click here

    ReplyDelete
  6. Hey Mike. Please to hear you are back to blogging.

    ReplyDelete
  7. right. Brain fart. There is no difference between foreach and enumerator move next after the compiler is done with it. Good catch Simon, and sorry Mike.

    ReplyDelete

Note: only a member of this blog may post a comment.