Skip to content

Instantly share code, notes, and snippets.

@NOtherDev
Last active December 12, 2015 08:49
Show Gist options
  • Save NOtherDev/4746753 to your computer and use it in GitHub Desktop.
Save NOtherDev/4746753 to your computer and use it in GitHub Desktop.
Extension method for ordering an IEnumerable<T> with custom ranking. See blog post for description: http://notherdev.blogspot.com/2013/02/extension-for-ordering-enumerable-with-ranking.html
// Extension method for ordering an IEnumerable<T> with custom ranking.
// http://notherdev.blogspot.com/2013/02/extension-for-ordering-enumerable-with-ranking.html
public class Ranked<T>
{
public Ranked(T item, int rank)
{
Item = item;
Rank = rank;
}
public T Item { get; private set; }
public int Rank { get; private set; }
}
public class SortDefinition<T>
{
public SortDefinition(Func<T, object> selector, bool isDescending = false)
{
Selector = selector;
IsDescending = isDescending;
}
public Func<T, object> Selector { get; private set; }
public bool IsDescending { get; private set; }
}
public static class OrderAndRankByEnumerableExtension
{
public static IEnumerable<Ranked<T>> OrderAndRankBy<T>(
this IEnumerable<T> source,
IEnumerable<SortDefinition<T>> sortAndRank,
IEnumerable<SortDefinition<T>> sortOnly)
{
if (source == null)
throw new ArgumentNullException("source");
var sortAndRankArray = sortAndRank.ToArray();
var allSorters = sortAndRankArray.Concat(sortOnly).ToArray();
if (allSorters.Length == 0)
return CreateRanking(source, EqualityComparer<T>.Default);
var ordered = SortSequence(source, allSorters);
return CreateRanking(ordered, new SortersEqualityComparer<T>(sortAndRankArray));
}
private static IOrderedEnumerable<T> SortSequence<T>(
IEnumerable<T> source, SortDefinition<T>[] sorters)
{
var ordered = sorters[0].IsDescending
? source.OrderByDescending(sorters[0].Selector)
: source.OrderBy(sorters[0].Selector);
for (var i = 1; i < sorters.Length; ++i)
{
var sorter = sorters[i];
ordered = sorters[i].IsDescending
? ordered.ThenByDescending(sorter.Selector)
: ordered.ThenBy(sorter.Selector);
}
return ordered;
}
private static IEnumerable<Ranked<T>> CreateRanking<T>(
IEnumerable<T> ordered, IEqualityComparer<T> comparer)
{
using (var it = ordered.GetEnumerator())
{
if (!it.MoveNext())
yield break;
// return first value with rank 1
var current = it.Current;
var ranked = new Ranked<T>(current, 1);
yield return ranked;
var previous = ranked;
var nextInOrder = 2;
while (it.MoveNext())
{
// and all the other values with either previous value for equal items
// or next rank in order
current = it.Current;
ranked = new Ranked<T>(current, comparer.Equals(previous.Item, current)
? previous.Rank
: nextInOrder);
yield return ranked;
previous = ranked;
++nextInOrder;
}
}
}
private class SortersEqualityComparer<T> : EqualityComparer<T>
{
private readonly SortDefinition<T>[] _sorters;
public SortersEqualityComparer(IEnumerable<SortDefinition<T>> sorters)
{
_sorters = sorters.ToArray();
}
public override bool Equals(T x, T y)
{
return _sorters.All(s => s.Selector(x).Equals(s.Selector(y)));
}
public override int GetHashCode(T obj)
{
return obj == null ? 0 : obj.GetHashCode();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment