Last active
December 12, 2015 08:49
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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