Skip to main content

6 useful extensions for IEnumerable

5/23/2023
4 minute read

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:

csharp
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.

csharp
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:

csharp
var (even, odd) = numbers.Partition(n => n % 2 == 0);
Console.WriteLine($"Even numbers: {string.Join(", ", even)}");
Console.WriteLine($"Odd numbers: {string.Join(", ", odd)}");

Output:

no-class
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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.

csharp
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:

csharp
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.

csharp
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:

csharp
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.

An error has occurred. This application may no longer respond until reloaded.Reload x