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:

Simon said...

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

oskark said...

Cool stuff.

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

Mike Hadlow said...

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.

Simon said...

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.

Larry Smith said...

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

For several examples, click here

Richard OD said...

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

oskark said...

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.