Extension Methods Roundup: IndicesWhere, TakeEvery, Distinct

As I do from time to time, here is a batch of three Extension Methods I've written recently:

IndicesWhere

/// <summary>
/// Gets the indices where the predicate is true.
/// </summary>
public static IEnumerable<int> IndicesWhere<T>(this IEnumerable<T> enumeration, Func<T, bool> predicate)
{
    // Check to see that enumeration is not null
    if (enumeration == null)
        throw new ArgumentNullException("enumeration");

    // Check to see that predicate is not null
    if (predicate == null)
        throw new ArgumentNullException("predicate");

    int index = 0;

    foreach (T item in enumeration)
    {
        if (predicate(item))
            yield return index;

        index++;
    }
}

This is especially useful when you want to cache indices from an array, rather than the array itself. Here's an example:

var indicesWithValues = values.IndicesWhere(value => value != null);

TakeEvery

/// <summary>
/// Take items from 'startAt' every at 'hopLength' items.
/// </summary>
public static IEnumerable<T> TakeEvery<T>(this IEnumerable<T> enumeration, int startAt, int hopLength)
{
    // Check to see that enumeration is not null
    if (enumeration == null)
        throw new ArgumentNullException("enumeration");

    int first = 0;
    int count = 0;

    foreach (T item in enumeration)
    {
        if (first < startAt)
        {
            first++;
        }
        else if (first == startAt)
        {
            yield return item;

            first++;
        }
        else
        {
            count++;

            if (count == hopLength)
            {
                yield return item;

                count = 0;
            }
        }
    }
}

This is equivalent to an unbounded series of Skip(startAt).Take(1).Skip(hopLength).Take(1).Skip(hopLength)... Useful for when you, for instance, need only every other item in a list.

Distinct

It's really been pissing me off that there's no overload to Distinct that takes a delegate, which means I have to write a new class whenever my comparison isn't the default one. When talking about Anonymous Types, Distinct becomes useless. So here's an overload I can actually use:

private class EqualityComparer<T> : IEqualityComparer<T>
{
    public Func<T, T, bool> Comparer { get; internal set; }
    public Func<T, int> Hasher { get; internal set; }

    bool IEqualityComparer<T>.Equals(T x, T y)
    {
        return this.Comparer(x, y);
    }

    int IEqualityComparer<T>.GetHashCode(T obj)
    {
        // No hashing capabilities. Default to Equals(x, y).
        if (this.Hasher == null)
            return 0;

        return this.Hasher(obj);
    }
}

/// <summary>
/// Gets distinct items by a comparer delegate.
/// </summary>
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> enumeration, Func<T, T, bool> comparer)
{
    return Distinct(enumeration, comparer, null);
}

/// <summary>
/// Gets distinct items by comparer and hasher delegates (faster than only comparer).
/// </summary>
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> enumeration, Func<T, T, bool> comparer, Func<T, int> hasher)
{
    // Check to see that enumeration is not null
    if (enumeration == null)
        throw new ArgumentNullException("enumeration");

    // Check to see that comparer is not null
    if (comparer == null)
        throw new ArgumentNullException("comparer");

    return enumeration.Distinct(new EqualityComparer<T> { Comparer = comparer, Hasher = hasher });
}

I'll be integrating these methods into the Linq Extensions project soon enough. Good hunting. :)

6 Comments

  • nice. is there no limit to how many nice extension methods are possible?

  • Probably not... hmmm... *rubs beard thoughtfully* :)

  • For IndicesWhere, is there a reason for using Func rather than the semantically equivalent Predicate delegate?

    Predicate would be compatible with existing framework methods such as List.FindIndex.

    And I'd have thought it should take an IList argument rather than an IEnumerable, since the indices don't have any meaning on an IEnumerable.

  • Joe,

    Predicate and Func are equivalent. It's a matter of which you personally like best and I like to stick to Func and Action because I'm mostly aware of what the method is supposed to do and this is a more descriptive way to find out how (parameter count, types, return value, etc.)
    I actually had to go check what Predicate's signature looked like to answer you, so that kind of proves the point :)

    As far as I'm concerned when coding, indexes became important in IEnumerable the moment Take, Skip and other extension methods were introduced.

  • Your extension method Distinct really solved my problem.
    Thanks a lot !!!

  • I know this is a year old, but I stumbled upon this while trying to figure out how I can get Distinct() to take a lambda expression and this fit my needs perfectly. Well done Omer and thanks for posting this information.

Comments have been disabled for this content.