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);
}