I am trying to add a general-purpose TopN IEnumerable<T>
extension.
If the parameter is positive then it is the same as Take() but if it is negative then it should do the same as Take() but then keep yielding immediately sequential values that match the last value from Take(). (The same as SQL TOP n WITH TIES)
This is the code I have at the moment:-
public static class Test
{
public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN)
{
return TopN(source, topN, (v1, v2) => v1.Equals(v2));
}
public static IEnumerable<TSource> TopN<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
return topN >= 0
? source.Take(topN)
: TopNWithTiesIterator(source, -topN, comparer);
}
static IEnumerable<TSource> TopNWithTiesIterator<TSource>(this IEnumerable<TSource> source, int topN, Func<TSource, TSource, bool> comparer)
{
var lastItem = default(TSource);
foreach (var item in source)
{
if (topN-- > 0 || comparer(item, lastItem))
{
lastItem = item;
yield return item;
}
else
{
yield break;
}
}
}
}
and here is a sample of real-world usage and some other quick tests I tried:
if (TopN != 0)
{
var values = new[] { 1, 2, 2, 3 };
Debug.Assert(!values.TopN(0).Any());
Debug.Assert(!values.TopN(0, (v1, v2) => v1 == v2).Any());
Debug.Assert(values.TopN(1, (v1, v2) => v1 == v2).Count() == 1);
Debug.Assert(values.TopN(-1, (v1, v2) => v1 == v2).Count() == 1);
Debug.Assert(values.TopN(2, (v1, v2) => v1 == v2).Count() == 2);
Debug.Assert(values.TopN(-2, (v1, v2) => v1 == v2).Count() == 3);
Debug.Assert(values.TopN(2).Count() == 2);
Debug.Assert(values.TopN(-2).Count() == 3);
// This is how I really want to use it
summaries = summaries.TopN(TopN, (v1, v2) => v1.ClientValue + v1.AdviserValue == v2.ClientValue + v2.AdviserValue);
}
My question is about whether using Func<TSource, TSource, bool>
as a comparer is correct.
Should I be using IEqualityComparer<T>
or IEquatable<<T>
or something else?