More missing LINQ operators
Continuing an old post on missing LINQ operators, the wonders of extension methods allow us as developers to fill potential holes in LINQ operators. Whether it’s a Zip method (now included in .NET 4.0), or better methods for IComparer-based operators, I find myself adding more and more helpful LINQ operators, I wish were already in the framework.
Alternate
Do you ever want to weave two collections together, like shuffling a deck of cards? Well I know I do! Suppose we have this collection:
[1, 3, 5]
And this collection:
[2, 4, 6]
I’d like to create new collection that is the alternating items from the first and second list:
[1, 2, 3, 4, 5, 6]
Here’s the code to do it:
public static IEnumerable<TSource> Alternate<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) { using (IEnumerator<TSource> e1 = first.GetEnumerator()) using (IEnumerator<TSource> e2 = second.GetEnumerator()) while (e1.MoveNext() && e2.MoveNext()) { yield return e1.Current; yield return e2.Current; } }
Very simple, I iterate both enumerables at the same time, yielding the first, then second collection’s current item. So how is this useful? How about this action:
[Test] public void Word_play() { var source = new[] {"The", "quick", "brown", "fox"}; var result = source.Alternate(Spaces()).Aggregate(string.Empty, (a, b) => a + b); result.ShouldEqual("The quick brown fox "); } private IEnumerable<string> Spaces() { while (true) yield return " "; }
I cheated a little bit with an infinite sequence (the Spaces() method), but I found this method useful when I had to split, then reconstruct new sequences of strings.
Append
I really hate this syntax:
[Test] public void Bad_concat_method() { var ints = new[] {1, 2, 3}; var oneToFour = ints.Concat(Enumerable.Repeat(4, 1)); CollectionAssert.AreEqual(new[] { 1, 2, 3, 4 }, oneToFour.ToArray()); }
I want to just stick an item on the end of an existing collection, but I have to use this arcane Enumerable.Repeat method to do so. Instead, let’s create an operator that lets us tack an item on to the end of a collection:
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element) { using (IEnumerator<TSource> e1 = source.GetEnumerator()) while (e1.MoveNext()) yield return e1.Current; yield return element; }
Now our code becomes much easier to understand:
[Test] public void Easier_concat_with_append() { var ints = new[] {1, 2, 3}; var oneToFour = ints.Append(4); CollectionAssert.AreEqual(new[] { 1, 2, 3, 4 }, oneToFour.ToArray()); }
Prepend
Append wouldn’t be complete without the converse, Prepend, now would it?
public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource element) { yield return element; using (IEnumerator<TSource> e1 = source.GetEnumerator()) while (e1.MoveNext()) yield return e1.Current; }
Now putting something on the beginning of a list is easier as well:
[Test] public void Easier_concat_with_prepend() { var ints = new[] {1, 2, 3}; var zeroToThree = ints.Prepend(0); CollectionAssert.AreEqual(new[] { 0, 1, 2, 3 }, zeroToThree.ToArray()); }
Much more readable. I have a few more around replacing the IEqualityComparer