Archive

Posts Tagged ‘linq’

Ix Operators

June 12, 2011 Leave a comment

[Updated on 02 Jul 11 after the Rx official release: Renamed Run to ForEach]

[If you don’t know what Ix (Interactive Extensions for .NET) is, then I recommend reading my earlier post.]

There are very good resources (like this video) on Ix operators so I am not going to spend too much time on this but I am going to talk about a few of these operators (my top three) to show how the interactive extensions can make your code easier to write and read.

I will use unit tests to express the functionality I am expecting and will then provide potential solutions without using Ix, and finally showing how Ix would simplify the implementation.

For the code snippets, assume that all methods (excluding the test methods) live in a static class named MyEnumerableScrathPad.

EnumerableEx.Return

You have an object of type T and want an enumerable sequence of T, which includes that object only.

Test
[TestMethod]
public void ReturnTest()
{
    var item = new object();
    var returnedItem = MyEnumerableScratchPad.Return(item).Single();
    Assert.AreSame(item, returnedItem);
}
 

Legacy implementation
public static IEnumerable<T> Return<T>(T item)
{
    return new T[] { item };
}
 

Using Ix
public static IEnumerable<T> Return<T>(T item)
{
    return EnumerableEx.Return(item);
}

 


EnumerableEx.StartWith

You have an enumerable sequence of T and you want to add an instance of type T to the beginning of that sequence.

Test
[TestMethod]
public void StartWithTest()
{
    var count = 10;
    var zero = 0;
    var originalRange = Enumerable.Range(1, count);
    var newRange =  MyEnumerableScratchPad.StartWith(originalRange, zero);
    Assert.AreEqual(zero, newRange.First());
    Assert.AreEqual(count + 1, newRange.Count());
}
 
Legacy implementation #1
public static IEnumerable<T> StartWith<T>(IEnumerable<T> originalSequence, 
  T first)
{
    var list = originalSequence.ToList();
    list.Insert(0, first);
    return list.AsEnumerable();
}

This will make the test pass but this implementation may force you to iterate through the items unnecessarily because of the call to ToList. We were also lucky that this implementation passed the test. If you change the value of count from 10 to int.MaxValue – 1, the test will horribly fail with an OutOfMemoryException. So the idea of converting the enumerable to a list is not a good one.

 
Legacy implementation #2
public static IEnumerable<T> StartWith<T>(IEnumerable<T> originalSequence, 
  T first)
{
    yield return first;
    foreach (var item in originalSequence)
        yield return item;
}

This implementation does not suffer from the problem shown above and passes the test after a fairly long delay (around 10 seconds on my laptop), which is expected as a result of the call to Count, which will iterate through the sequence and one can argue that this is not a good test but it fits the purpose here.

 
Using Ix
public static IEnumerable<T> StartWith<T>(IEnumerable<T> originalSequence, 
T first)
{
    return originalSequence.StartWith(first);
}

Ix also has another overload for StartWith, which takes a param array of items to be inserted at the beginning of the sequence, which can be handy.

 

EnumerableEx.ForEach

You have an enumerable sequence and you want to perform an action for each item of the sequence.

[TestMethod]
public void ForEachTest()
{
    var list = new List<int>();
    var range = Enumerable.Range(1, 10);
    MyEnumerableScratchPad.ForEach(range, list.Add);
    CollectionAssert.AreEqual(range.ToList(), list);
}
 
Legacy implementation #1
public static void ForEach<T>(IEnumerable<T> sequence, Action<T> action)
{    foreach (var item in sequence)
    {
        action(item);
    }
}
Clearly this is a valid solution but we are looking for a more compact solution.
 
Legacy implementation #2
public static void ForEach<T>(IEnumerable<T> sequence, Action<T> action)
{
    Array.ForEach(sequence.ToArray(), action);
}
 
Legacy implementation #3
public static void ForEach<T>(IEnumerable<T> sequence, Action<T> action)
{
    sequence.ToList().ForEach(action);
}

 

Implementations #2 and #3 are not ideal as they both force the enumeration of the sequence before the iterating through the items begins. This is not necessarily a problem but there are some cases where you want to perform the action as soon as the sequence yields a new value.

 
Using Ix
public static void ForEach<T>(IEnumerable<T> sequence, Action<T> action)
{
    sequence.ForEach(action);
}
 

Because most of the Ix operators including ForEach are ported back from the Rx world, there are some interesting additional overloads for ForEach. For example, there is one that takes action of T (the action that needs to be performed on each item) and then another action that will be performed when the iteration over the sequence is completed.

 

These three operators (Return, StartWith and ForEach) are among the simplest of the long list of Ix operators but nonetheless they are the ones I use most as they make code more concise and easier to read.

Follow

Get every new post delivered to your Inbox.