I did already write about some useful extension methods for Task
and ValueTask
. Today I want to show you some useful extension methods for IEnumerable
.
Disclaimer
Those methods are extension methods for IEnumerable
. Be careful if your underlying type is something like IQueryable
because we might materialize the query here.
Instead of IEnumerable
you could use IReadOnlyCollection
so you know that you are not dealing with a queryable and your collection at hand is already materialized.
IsNullOrEmpty
Just like string
, we can introduce a IsNullOrEmpty
extension method for IEnumerable
:
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? source) => source is null || !source.Any();
var isEmpty = Enumerable.Empty<int>().IsNullOrEmpty(); // true
var isEmpty = new[] { 1, 2, 3 }.IsNullOrEmpty(); // false
var isEmpty = ((IEnumerable<int>)null).IsNullOrEmpty(); // true
Partitioning
Partitioning is a common operation on collections. We can partition a collection into two collections. One collection contains all elements that match a predicate and the other collection contains all elements that do not match the predicate.
public static (IEnumerable<T> True, IEnumerable<T> False) Partition<T>(
this IEnumerable<T> source,
Func<T, bool> predicate)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(predicate);
var trueItems = new List<T>();
var falseItems = new List<T>();
foreach (var item in source)
{
if (predicate(item))
trueItems.Add(item);
else
falseItems.Add(item);
}
return (trueItems, falseItems);
}
}
The usage would be like this:
var (even, odd) = numbers.Partition(n => n % 2 == 0);
Console.WriteLine($"Even numbers: {string.Join(", ", even)}");
Console.WriteLine($"Odd numbers: {string.Join(", ", odd)}");
Output:
Even numbers: 0, 2, 4
Odd numbers: 1, 3
Median
The median is the middle number in a sorted, ascending or descending, list of numbers and can be more descriptive of that data set than the average. LINQ itself ships the Average
extension method but not the Median
extension method. We can easily add it:
public static double Median<T>(this IEnumerable<T> source) where T : IConvertible
{
ArgumentNullException.ThrowIfNull(source);
var sortedList = source.Select(x => x.ToDouble(CultureInfo.InvariantCulture)).OrderBy(x => x).ToList();
var count = sortedList.Count;
if (count == 0)
{
throw new InvalidOperationException("The source sequence is empty.");
}
if (count % 2 == 0)
{
return (sortedList[count / 2 - 1] + sortedList[count / 2]) / 2;
}
return sortedList[count / 2];
}
Usage:
var median = new[] { 1, 1, 1, 1, 5, 6, 7, 8, 9 }.Median(); // 5
var average = new[] { 1, 1, 1, 1, 5, 6, 7, 8, 9 }.Average(); // 4.333333333333333
Mode
The mode is the number that is repeated most often in a set of numbers. We can easily add it:
public static IEnumerable<T> Mode<T>(this IEnumerable<T> source)
{
ArgumentNullException.ThrowIfNull(source);
var groups = source.GroupBy(x => x);
var maxCount = groups.Max(g => g.Count());
return groups.Where(g => g.Count() == maxCount).Select(g => g.Key);
}
Usage:
var mode = new[] { 1, 1, 1, 1, 5, 6, 7, 8, 9 }.Mode(); // 1
If two numbers are repeated the same amount of times, we get both numbers!
StandardDeviation
The standard deviation is a measure of how spread out numbers are.
public static double StandardDeviation<T>(this IEnumerable<T> source) where T : IConvertible
{
ArgumentNullException.ThrowIfNull(source);
var values = source.Select(x => x.ToDouble(CultureInfo.InvariantCulture)).ToList();
var count = values.Count;
if (count == 0)
{
throw new InvalidOperationException("The source sequence is empty.");
}
var avg = values.Average();
var sum = values.Sum(d => Math.Pow(d - avg, 2));
return Math.Sqrt(sum / count);
}
Usage:
var standardDeviation = new[] { 1, 1, 1, 1, 5, 6, 7, 8, 9 }.StandardDeviation(); // 3.1622776601683795
Shuffle
This method shuffles the elements in a sequence using Fisher-Yates shuffle algorithm.
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
ArgumentNullException.ThrowIfNull(source);
var elements = source.ToArray();
var random = Random.Shared;
for (var i = elements.Length - 1; i > 0; i--)
{
var swapIndex = random.Next(i + 1);
(elements[i], elements[swapIndex]) = (elements[swapIndex], elements[i]);
}
return elements;
}
Usage:
var numbers = Enumerable.Range(0, 5);
var random = numbers.Shuffle();
// 4, 0, 1, 3, 2
Console.WriteLine(string.Join(", ", random));
Conclusion
Today we saw some useful extension methods for IEnumerable
. I hope you find them useful. If you have any questions or feedback feel free to leave a comment below.