Los Techies : Blogs about software and anything tech!

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<T> overloads, but those are a little bit more esoteric in their examples of crazy set-based logic.  What’s really cool about all these methods is they still allow all sorts of fun chaining, allowing me to create very terse chains of operations on lists, with what would have taken a bazillion cryptic for..each loops.  Cool stuff!

Kick It on DotNetKicks.com
Posted Oct 15 2009, 11:19 PM by bogardj
Filed under:

Comments

Reflective Perspective - Chris Alcock » The Morning Brew #456 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #456
on 10-16-2009 3:16 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #456

Richard Dingwall wrote re: More missing LINQ operators
on 10-16-2009 5:30 AM

Nice, someone should get a project going for extra operators like these. Traverse() is another favourite of mine for flattening recursive hierarchies: social.msdn.microsoft.com/.../fe3d441d-1e49-4855-8ae8-60068b3ef741

Joshua Flanagan wrote re: More missing LINQ operators
on 10-16-2009 8:18 AM

I can see why you would hate the Enumerable.Repeat syntax, but not sure what would drive you to use it (except counting clock cycles).

var oneToFour = ints.Concat(new[]{4});

Not as pretty as Append, but definitely an improvement over Enumerable.Repeat, in my opinion.

bogardj wrote re: More missing LINQ operators
on 10-16-2009 9:12 AM

@Josh

Ha, yeah we use that way too especially in the overloads that take collections, but we just want to work with a single item.  Concat, Union, Except, etc etc.

Diogo Mafra wrote re: More missing LINQ operators
on 10-16-2009 9:24 AM

In the first example you could use String.Join:

String.Join(" ", source)

I would like to have an extension method to verify if a string is null or empty ignoring spaces:

(name == null || name.Trim().Length == 0)

Twitter Trackbacks for More missing LINQ operators - Jimmy Bogard - Los Techies : Blogs about software and anything tech! [lostechies.com] on Topsy.com wrote Twitter Trackbacks for More missing LINQ operators - Jimmy Bogard - Los Techies : Blogs about software and anything tech! [lostechies.com] on Topsy.com
on 10-16-2009 9:30 AM

Pingback from  Twitter Trackbacks for                 More missing LINQ operators - Jimmy Bogard - Los Techies : Blogs about software and anything tech!         [lostechies.com]        on Topsy.com

Will wrote re: More missing LINQ operators
on 10-16-2009 11:04 AM

Alternate:

Interesting, yet the example doesn't seem like a good fit.  As Diogo pointed out, you can simply use String.Join if the alternating value never changes.

Append / Prepend

I really like using the "params" keyword in situations like this.  It allows me to call on a method more naturally and the compiler composes the collection (array) for me.  Plus, in these examples, you would be able to append or prepend multiple values.

e.g.

public static IEnumerable<TSource> Append<TSource>( this IEnumerable<TSource> source, params TSource[] elements )

{

   return source.Concat( elements );

}

var start = new[] { 1, 2, 3 };

var result = new[] { 1, 2, 3, 4, 5 };

start.Append( 4, 5 ).ShouldHaveSameElements( result );

However, I've never gone this far.  I've been content with Joshua's suggestion of new[]{x}.

bogardj wrote re: More missing LINQ operators
on 10-16-2009 11:45 AM

@Will, @Diogo

The real-life example is a split-join scenario, where the other joined piece is a list of custom items.  It's a template thing, transforming:

"Hello [blank], today is [blank]" to "Hello Joe, today is Thursday"

Jarrett Meyer wrote re: More missing LINQ operators
on 10-16-2009 1:20 PM

I find myself constantly wanting all of the array operations from Python to be available in .NET (without rewriting them myself).

Friday Links #72 | Blue Onion Software * wrote Friday Links #72 | Blue Onion Software *
on 10-16-2009 7:47 PM

Pingback from  Friday Links #72 | Blue Onion Software *

Is Functional Abstraction Too Clever? « Solutionizing .NET wrote Is Functional Abstraction Too Clever? &laquo; Solutionizing .NET
on 10-17-2009 3:29 AM

Pingback from  Is Functional Abstraction Too Clever? « Solutionizing .NET

Solutionizing .NET (Keith Dahlby) wrote Is Functional Abstraction Too Clever?
on 10-17-2009 3:32 AM

I received a rather interesting comment on a recent Stack Overflow answer: This code seems too clever

DotNetBurner - Linq wrote More missing LINQ operators
on 10-17-2009 3:44 PM

DotNetBurner - burning hot .net content

DotNetShoutout wrote More missing LINQ operators - Jimmy Bogard - Los Techies
on 10-19-2009 9:40 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Tim Van Wassenhove » Archive » Party for one wrote Tim Van Wassenhove &raquo; Archive &raquo; Party for one
on 10-29-2009 4:11 AM

Pingback from  Tim Van Wassenhove  » Archive  » Party for one

Everett Muniz wrote re: More missing LINQ operators
on 10-29-2009 10:45 PM

Thanks for sharing this.  This is pretty incidental but I wonder if a clearer, more accurate name for Alternate would be Interlace.

bogardj wrote re: More missing LINQ operators
on 10-30-2009 8:35 AM

@Everett

That's good too!  I thought of Zip, but that was already taken :P  I think "Splice" might be a good term too?

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems