Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

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. :)

Comments

secretGeek said:

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

# May 18, 2008 9:33 AM

Omer van Kloeten said:

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

# May 18, 2008 10:01 AM

Jason Haley said:

# May 18, 2008 10:39 AM

Joe said:

For IndicesWhere, is there a reason for using Func<T, bool> rather than the semantically equivalent Predicate<T> delegate?  

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

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

# May 18, 2008 10:47 AM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# May 18, 2008 11:15 AM

Omer van Kloeten said:

Joe,

Predicate<T> and Func<T, bool> 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<T>'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<T> the moment Take, Skip and other extension methods were introduced.

# May 18, 2008 12:45 PM

Dew Drop - May 18, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - May 18, 2008 | Alvin Ashcraft's Morning Dew

# May 18, 2008 9:15 PM

Thurein said:

Your extension method Distinct really solved my problem.

Thanks a lot !!!

# September 20, 2008 1:33 PM

David said:

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.

# July 9, 2009 3:07 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)