I’m a bit late to the party with this one – Ix or Interactive Extensions.
Available on NuGet via the Ix-Main package. This will give us the System.Interactive assembly.
Ix are basically a bunch of extension methods for IEnumerable and the
EnumerableEx class. I’m sure many of us have written IEnumerable extensions to supply a ForEach extension method or the likes. Well Ix gives us that and more.
In this post I will not be documenting each overload but will try to give an idea of what the methods might be used for and how to use them. Checking which overload is suitable is down to the reader.
Note: some of the examples may be less than useful, I will attempt to update with better examples as/when I come up with them.
Buffer
Basically takes an IEnumerable and creates an IEnumerable of IList of type T of the given buffer size.
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76, 45, 32, 1, 6, 3, 89, 100 };
IEnumerable<IList<int>> lists = items.Buffer(3);
The above would result in an IEnumerable with 4 ILists of type T each containing up to 3 items each. An overload of this method exists which allows you to skip a number of elements at the start of each buffer.
Case
Case is a static method on EnumerableEx and allows us to pass in an argument to compare against and an IDictionary of keys – which will be used to match with the argument passed into the Case method and it will return an IEnumerable relating to the values which appear in the dictionary against the given key. So for example
IDictionary<int, IEnumerable<string>> d = new Dictionary<int, IEnumerable<string>>
{
{5, new[] {"Five"}},
{4, new[] {"Four"}},
{1, new[] {"One"}},
{2, new[] {"Two"}},
{3, new[] {"Three"}},
};
var matches = EnumerableEx.Case(() => 4, d);
In the above, matches will contain the IEnumerable of the values stored in the dictionary against the key 4, in other words the IEnumerable containing the string “Four”.
An overload of this method allows you to supply an IEnumerable to act as the default values should a match against the selector not be found.
Create
Create is a static method on the EnumerableEx class which allows us to create an IEnumerable from a IEnumerator (or via an overload from an IYielder).
Let’s assume we have a method that returns an IEnumerator of integers, here’s a mock up of such a method
private static IEnumerator<int> Factory()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}
Now using Ix we can create an IEnumerable of integers using
var items = EnumerableEx.Create(Factory);
Defer
As the name suggests Defer will defer the creation of an enumerable sequence until a call to GetEnumerator, so for example
var items = EnumerableEx.Defer(() => new [] { 3, 5, 6, 2 });
the code above will not call the enumerable factory method (in this case the anonymous method creating an array of integers) until the GetEnumerator is called.
Catch
Creates an IEnumerable sequence based on the source, but if the source has an exception will switch to the second IEnumerable.
A somewhat convoluted sample below
IEnumerable<int> GetNegatives()
{
int[] items = new int[] { -1, -2, 0, -3 };
for (int i = 0; i < items.Length; i++)
{
if (items[i] >= 0)
throw new Exception();
yield return items[i];
}
}
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76, 45, 32, 1, 6, 3, 89, 100 };
IEnumerable<int> e = GetNegatives().Catch(items);
The above will create an IEnumerable e that contains -1, -2 and then the values from the items IEnumerable.
There are several overloads of the Catch method to check out.
Distinct
Returns an IEnumerable of the distinct values in a sequence
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76, 45, 32, 1, 6, 3, 89, 100 };
IEnumerable<int> distinct = items.Distinct();
The above will result in a sequence 3, 5, 6, 2, 76, 45, 32, 1, 89, 100 removing the duplicates.
DistinctUntilChanged
Returns consecutive distinct values
IEnumerable<int> items = new int[] { 3, 3, 3, 5, 5, 2, 76, 76, 100 };
IEnumerable<int> distinct = items.DistinctUntilChanged();
The above will return an sequence 3, 5, 2, 76, 100
Do
Several overloads of the Do method exist, we’ll just take a look at the one which takes an Action. Do will simply invoke some action on each item within the IEnumerable, so for example
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76 };
items.Do(Console.WriteLine);
the above code will simply call WriteLine on each integer within the items IEnumerable. However, this is lazy and hence we actually need to do something with the items returns enumerable.
DoWhile
The DoWhile method iterates over an IEnumerable whilst the supplied function is true, so for example
var items = new int[] { 3, 5, 6, 2 };
int i = 0;
var results = items.DoWhile(() => i++ < 2);
int len = results.Count();
If we run this code the len variable will be 12 as the DoWhile looped through the IEnumerable 3 times and the items array contained four items. So basically if we output to the console each item returned by results will see the array items output three times.
Expand
Expand, basically loops through the supplied IEnumerable (possibly infinitely if you do not use a Take or similar method to stop it). In essence is applies a selector function each time through the enumerable changing the values.
So imagine we have an array of values 1, 2 and 0 and apply Expand to this as follows
var items = new [] { 1, 2, 0 };
var results = items.Expand(i => new[] {i + 1}).Take(9);
what happens here is the output (if we ran results.ForEach(Console.WriteLine)) will output 1, 2, 0 (the values from items then 2, 3, 1 and finally 3, 4, 2. As you see each time we iterate through we add 1 to each element.
Finally
Finally can be used on a sequence so that an action may be invoked upon the termination or disposal, so for example the following will output the sequence to the console then output “Completed”
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
items.Finally(() => Console.WriteLine("Completed")).ForEach(Console.WriteLine);
For
The For method takes a source enumerable and applies a supplied enumerable to it, so a simple example might be
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76 };
EnumerableEx.For(items, i => new []{i + 1, i + 2});
In this case each item in the items enumerable will have the new []{i + 1, i + 2} applied to it, thus output for this would be
4, 5, 6, 7, 7, 8, 3, 4, 77, 78
the first item 3 in the source enumerable is sent to the For method and we get back a transformed value as two values 3 + 1 and then 3 + 2 and so on.
ForEach
ForEach will invoke an action on each item within an IEnumerable, so a simple example might be to just write a more compact foreach to write output to the console, i.e.
IEnumerable<int> items = new int[] { 3, 5, 6, 2, 76 };
items.ForEach(Console.WriteLine);
Generate
As the name suggests, Generate can be used to generate a sequence in a similar way to a for loop might be used, here’s an example which creates an IEnumerable with the range [10, 20]
EnumerableEx.Generate(10, i => i <= 20, i => i + 1, i => i)
The first argument is the starting value, then we have the condition to stop the generator, next we have the state update function, in this case we’re incrementing the state by 1, finally we have the result function, in this case we’re just using the supplied state value.
Hide
To quote the documentation “Returns Enumerable sequence with the same behavior as the original, but hiding the source identity”.
I’m not wholly sure of the use cases for this, but basically if we have the following
List<int> items = new List<int> { 3, 5, 6, 2, 76 };
if we used items.AsEnumerable() the type returned would still be a List however using
var result = items.Hide();
result will be a funky EnumerableEx type hiding the underlying implementation.
If
The If method is self-explanatory, it allows us to return an IEnumerable if a condition is met or it will return an empty sequence or the overload shown below acts as an if..else returning the alternate sequence
EnumerableEx.If(() => someCondition, new[] { 3, 5, 6, 2, 76 }, new [] {6, 7});
So it’s probably obvious that if the someCondition is true the first array is returned else the second array is returned.
IgnoreElements
A slightly odd one, which probably is more useful when used in combination with other methods. IgnoreElements returns a source sequence without its elements
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.IgnoreElements();
result will be an empty sequence.
IsEmpty
As the name suggests, checks if the sequence is empty or not, obviously the following is not empty
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.IsEmpty();
whereas the following will be empty
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.IgnoreElements().IsEmpty();
Max
Returns the maximum value in the sequence
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.Max();
this will result in the result being 76.
MaxBy
In essence MaxBy returns a sequence of values less than the supplied comparer, for example
IEnumerable<int> items = new [] { 3, 50, 6, 2, 76 };
items.MaxBy(i => i < 50).ForEach(Console.WriteLine);
this will create a sequence with values less than 50 and in this case write them to the console, thus outputting the values 3, 6 and 2.
Memoize
Memoize creates a buffer over the source sequence to ensure that if we were to iterate over the items multiple times we would not call the source multiple times. Obviously this would be useful if we’ve got the data from some file system or remote source to stop us retrieving the data multiple times, in use we have
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.Memoize();
Min
Returns the minimum value in the sequence
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
var result = items.Max();
this will result in the result being 2.
MinBy
In essence MinBy returns a sequence of values greater than the supplied comparer, for example
IEnumerable<int> items = new [] { 3, 50, 6, 2, 76 };
items.MinBy(i => i < 50).ForEach(Console.WriteLine);
this will create a sequence with values greater or equal to 50 and in this case write them to the console, thus outputting the values 50 and 76.
OnErrorResumeNext
Concatenates the sequence with a second sequence regardless of whether an error occurred, used in situation where you might be getting data from a source that could fail, the example below just shows the syntax really as this wouldn’t need to use OnErrorResumeNext
IEnumerable<int> items = new [] { 3, 50, 6, 2, 76 };
var result = items.OnErrorResumeNext(new[] {9, 10});
result would contain the items sequence followed by the sequence { 9, 10 }.
Publish
To quote the documentation, “creates a buffer with a view over the source sequence, causing each enumerator to obtain access to the remainder of the sequence from the current index in the buffer”.
This allows the sequence to be shared via the buffer, in syntax terms this looks like
IEnumerable<int> items = new [] { 3, 50, 6, 2, 76 };
var buffer = items.Publish();
There is an overloaded method which allows you to supply a selector.
Repeat
Allows us to iterate through a sequence infinitely or with the overload, n times
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
// iterate over three times
items.Repeat(3).ForEach(Console.WriteLine);
// iterate infinitely
items.Repeat().ForEach(Console.WriteLine);
Retry
This method allows us to retry enumerating a sequence whilst an error occurs or via the overload we can specify the maximum number of retries
items.Retry(2);
Return
The Return method returns a single element as a sequence, for example
EnumerableEx.Return(1);
this creates an IEnumerable of integers with the single value 1 in it.
Scan
Scan generates a sequence using an accumulator, so for example
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
items.Scan((i, j) => i + j).ForEach(Console.WriteLine);
will pass 3 (i) and 5 (j) into the accumulator method the result will be 8 this will then be pass into the accumulator as i followed by 6 (j) and so on.
SelectMany
Several overloads exist, the one shown here simply projects each element of the sequence with a given sequence. For example the following simply projects the array { 5, 6 } over the original sequence
IEnumerable<int> items = new [] { 3, 50, 6, 2, 76 };
items.SelectMany(new [] { 5, 6 }).ForEach(Console.WriteLine);
This will output { 5, 6 } the number of times matching the number of elements in the items sequence, i.e. 5 times.
Share
A couple of overloads. This method creates a buffer with a shared view over the sequence so mutiple enumerators can be used to fetch the next element from the sequence, this example is a little convoluted
IEnumerable<int> items = new [] { 20, 10, 60, 30 };
items.Share(x => x.Zip(x, (a, b) => a - b)).ForEach(Console.WriteLine);
The same sequence is used on Zip and passed into Zip hence we’re zipping the sequence with itself, the result selector simply subtracts one value from the other. When output to the console this will write a sequence 10, 30 because the first item (20) has the second item (10) subtracted from it, as then the next item (60) has the final item (30) subtracted.
SkipLast
Allows us to skip/bypass n elements from the end of in the sequence, thus
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
items.SkipLast(2).ForEach(Console.WriteLine);
lists values 3, 5, 6 but not the last 2 items.
StartWith
StartWith starts the sequence with the newly supplied enumerable, for example
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
items.StartWith(new []{9, 10}).ForEach(Console.WriteLine);
will output the sequence 9, 10 then follow with the items sequence.
TakeLast
In essence the opposite of SkipLast, TakeLast results in a sequence on the last n items, such as
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
items.TakeLast(2).ForEach(Console.WriteLine);
which outputs 2 and 76.
Throw
With the Throw method we can create a sequence which throws an exception when we try to enumerate it, for example
EnumerableEx.Throw<int>(new NullReferenceException()).ForEach(Console.WriteLine);
will throw the NullReferenceException when ForEach attempts to enumerate the sequence.
Using
Using creates a sequence which has a resource who’s lifetime is determined by the sequence usage. The idea being the resource is created via the Using method’s first argument then passed to the second argument (in all likelihood to be used in some way) before an IEnumerable is returned. The key thing to note is the resource is not created until we use the returned IEnumerable, for example
IEnumerable<int> items = new [] { 3, 5, 6 };
IEnumerable<int> results = EnumerableEx.Using(() => new SomeDisposable(), resource => items);
results.ForEach(Console.WriteLine);
Assuming SomeDisposable implements IDisposable, when we call GetEnumerator the SomeDisposable is created and passed to the second argument – this rather simplistic example does nothing with the resource but hopefully you get the idea. We then return the IEnumerable and when the GetEnumerator completes the resources Dispose method is called.
While
Loops through the sequence whilst a condition is true, so for example
IEnumerable<int> items = new [] { 3, 5, 6, 2, 76 };
int i = 0;
EnumerableEx.While(() => i++ < 3, items).ForEach(Console.WriteLine);
will output the items sequence three times.