Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get top N elements of a list with LinQ?

Tags:

c#

linq

I have an ordered list by exam points and I want to have the top N element of this list.
If the N(th) and N+1(th) students have the same exam points, the list must have them both.

For example I have a list like this:

john.   80  
mike.   75  
james.  70  
ashley. 70
kate.   60

Top 3 should return john, mike, james, ashley
I tried Take() but it returns only john, mike, james

English is not my main language, sorry if I couldn't tell correctly
Thanks

like image 611
AhmetEmre90 Avatar asked Sep 29 '14 12:09

AhmetEmre90


1 Answers

Here's a one-pass-only implementation:

public static IEnumerable<TSource> TopWithTies<TSource, TValue>(
    this IEnumerable<TSource> source,
    int count,
    Func<TSource, TValue> selector)
{
    if (source == null) throw new ArgumentNullException("source");
    if (selector == null) throw new ArgumentNullException("selector");
    if (count < 0) throw new ArgumentOutOfRangeException("count");
    if (count == 0) yield break;
    using(var iter = source.OrderByDescending(selector).GetEnumerator())
    {
        if(iter.MoveNext())
        {
            yield return iter.Current;
            while (--count >= 0)
            {
                if(!iter.MoveNext()) yield break;
                yield return iter.Current;    
            }
            var lastVal = selector(iter.Current);
            var eq = EqualityComparer<TValue>.Default;
            while(iter.MoveNext() && eq.Equals(lastVal, selector(iter.Current)))
            {
                yield return iter.Current;
            }
        }
    }
}

Example usage:

var data = new[]
{
    new { name = "john", value = 80 },
    new { name = "mike", value = 75 },
    new { name = "james", value = 70 },
    new { name = "ashley", value = 70 },
    new { name = "kate", value = 60 }
};
var top = data.TopWithTies(3, x => x.value).ToList();
foreach(var row in top)
{
    Console.WriteLine("{0}: {1}", row.name, row.value);
}
like image 143
Marc Gravell Avatar answered Sep 30 '22 00:09

Marc Gravell