Interactive Extensions for LINQ to Objects
Interactive Extensions (Ix) is a set of additional LINQ to Objects query operators based on the work done in the Reactive Extensions (Rx). A quick look through the API reveals a set of IEnumerable extension methods under the System.Linq namespace. While most developers already have many of these in their own utility libraries, having a standard implementation for these missing features would be useful.
- First up is ForEach, the extension method that pretty much every developer has written at some point. There are two variants, with the second including a zero-based index.
- Buffer will chop up an enumeration into smaller enumerations based on a given size. The buffer method uses a mixture of eager and lazy loading. Each buffer of N elements is eagerly read, but no more elements from the source will be read until the previous buffer is fully consumed.
- Catch is a rather interesting function. It will read from one source until it either completes or it throws an exception. In the latter case it will then start reading from a second source. The second source can be either an IEnumerable or a function that will return an IEnumerable. When using the latter the logic can be restricted to a specific type of exception.
- Concat merely starts enumerating a second list once the first is completed.
- Defer takes a function instead of an IEnumerable. The function will be executed if and only if something tries to enumerate over it.
- DistinctUntilChanged filters out consecutive duplicates. For example, the sequence [A A B A A B] would become [A B A B].
- Do is a variant of ForEach. Unlike ForEach, the Do function only executes its action on elements as they are being enumerated.
- DoWhile is like TakeWhile except that the predicate function doesn’t consider the item being enumerated.
- The For function deals with an interesting problem. Say you have a list of authors and a matching search function that returns a list of books. The For function would execute the search function on each author in the list and then return a concatenated list of books.
- Generate returns a list my mimicking a C style for-loop. It takes for parameters, the initial value, the condition function, the iterate function that changes the state, and the result selector. One could see easily this being used to represent a state machine as an IEnumerable.
- The If function uses a predicate function to determine which of two lists to return. If only one list is supplied, then an empty sequence is returned for the false case.
- Memoize is used to prevent enumerating a source more than once. In some ways it is like calling ToList, except that ToList will always enumerate the source only once while Memoize will enumerate the source zero or one times. If you tell Memoize how many readers to expect, it can remove items from its internal buffer as the items are consumed by all of the expected readers.
- Min and Max work as advertised. MinBy and MaxBy add the option of supplying a value function so you can express things such as “select the customer with the most sales”.
- OnErrorResumeNext concatenates two or more sequences with the added benefit that an error from any one sequence will not prevent it from concatenating the remaining sequences.
- Repeat creates a list, finite or infinite, that simply repeats the same value over and over again. The value may be a scalar or a list.
- Retry will attempt to enumerate a source until successful or an optional number of exceptions have been thrown.
- Return will turn a scalar value into a list containing just that one value, eliminating the need to explicitly create arrays of length 1.
- Scan is a useful feature for aggregating multiple fields. Unlike normal aggregate functions, Scan functions can be composed, this reducing the number of passes through the source. For example, source.Scan(a=> Dollars +=a.Dollars).Scan(a=> Units +=a.Units).
- SkipLast and TakeLast are mirrors of the Take and Skip functions.
- Throw is useful for testing, it creates an IEnumerable that will throw an exception if someone tries to enumerate over it.
That's the kind of stuff that make me love .NET
C# feels and performs so good being Object Oriented and almost Functional at the same time. It rocks!