考虑以下代码:
class Results
{
public int playerId;
public int score;
public int section;
public int position;
public Results(int _playerId, int _score, int _section)
{
playerId = _playerId;
score = _score;
section = _section;
}
}
public void RankMyResults()
{
List<Results> myResultList = new List<Results>();
myResultList.Add(new Results(1,232, 1));
myResultList.Add(new Results(2,213, 1));
// Add a lot of more results
// Iteriate over the items to set the position
}
我想在每个部分中将位置1设置为最高分,将第2个位置设置为第二高,依此类推。
如果两个人的得分相同,那么这个位置应该是这样的
Position Score PlayerId Section
1 135 23 1
1 135 43 1
3 131 45 1
如本例所示,它将跳过第2位。
有没有一种很好的方法可以使用LINQ来执行此操作,或者使用List对象中的某些Select,Sorting功能?
我自己的解决方案迭代列表并不是很好!
答案 0 :(得分:8)
几天前我写了这些扩展方法:
#region RankBy
public static IEnumerable<TResult> RankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, int, TResult> resultSelector)
{
return source.RankBy(keySelector, null, false, resultSelector);
}
public static IEnumerable<TResult> RankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
Func<TSource, int, TResult> resultSelector)
{
return source.RankBy(keySelector, comparer, false, resultSelector);
}
public static IEnumerable<TResult> RankByDescending<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
Func<TSource, int, TResult> resultSelector)
{
return source.RankBy(keySelector, comparer, true, resultSelector);
}
public static IEnumerable<TResult> RankByDescending<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, int, TResult> resultSelector)
{
return source.RankBy(keySelector, null, true, resultSelector);
}
private static IEnumerable<TResult> RankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
bool descending,
Func<TSource, int, TResult> resultSelector)
{
comparer = comparer ?? Comparer<TKey>.Default;
var grouped = source.GroupBy(keySelector);
var ordered =
descending
? grouped.OrderByDescending(g => g.Key, comparer)
: grouped.OrderBy(g => g.Key, comparer);
int totalRank = 1;
foreach (var group in ordered)
{
int rank = totalRank;
foreach (var item in group)
{
yield return resultSelector(item, rank);
totalRank++;
}
}
}
#endregion
#region DenseRankBy
public static IEnumerable<TResult> DenseRankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, int, TResult> resultSelector)
{
return source.DenseRankBy(keySelector, null, false, resultSelector);
}
public static IEnumerable<TResult> DenseRankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
Func<TSource, int, TResult> resultSelector)
{
return source.DenseRankBy(keySelector, comparer, false, resultSelector);
}
public static IEnumerable<TResult> DenseRankByDescending<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
Func<TSource, int, TResult> resultSelector)
{
return source.DenseRankBy(keySelector, comparer, true, resultSelector);
}
public static IEnumerable<TResult> DenseRankByDescending<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, int, TResult> resultSelector)
{
return source.DenseRankBy(keySelector, null, true, resultSelector);
}
private static IEnumerable<TResult> DenseRankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
bool descending,
Func<TSource, int, TResult> resultSelector)
{
comparer = comparer ?? Comparer<TKey>.Default;
var grouped = source.GroupBy(keySelector);
var ordered =
descending
? grouped.OrderByDescending(g => g.Key, comparer)
: grouped.OrderBy(g => g.Key, comparer);
int rank = 1;
foreach (var group in ordered)
{
foreach (var item in group)
{
yield return resultSelector(item, rank);
}
rank++;
}
}
#endregion
您可以按如下方式使用它们:
var rankedPlayers = players.RankByDescending(
p => p.Score,
(p, r) => new { Rank = r, Player = p });
RankBy
和DenseRankBy
之间的区别在于RankBy
创建了“空白”(例如1,1,3,3,3,6 ......)而DenseRankBy
不(1,1,2,2,2,3 ......)
答案 1 :(得分:0)
我修改了上面那些好方法,以便保留原始顺序。排名方法不应该改变元素的顺序,它们应该只对它们进行排名并以输入集合的原始顺序返回元素。
private static IEnumerable<TResult> RankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
bool descending,
Func<TSource, int, TResult> resultSelector)
{
var comp0 = comparer ?? Comparer<TKey>.Default;
var comp = descending ? Comparer<TKey>.Create((x, y) => -comp0.Compare(x, y)) : comp0;
var keys = source.Select(x => keySelector(x)).ToArray();
var indexes = Enumerable.Range(0, keys.Length).ToArray();
Array.Sort<TKey, int>(keys, indexes, comp);
var groups = new int[keys.Length];
int group = 0;
int index = 0;
for (int j = 1; j < keys.Length; ++j)
{
++index;
if (comp.Compare(keys[j], keys[j - 1]) != 0)
{
group += index;
index = 0;
}
groups[indexes[j]] = group;
}
index = 0;
foreach (var item in source)
{
yield return resultSelector(item, groups[index++] + 1);
}
}
private static IEnumerable<TResult> DenseRankBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IComparer<TKey> comparer,
bool descending,
Func<TSource, int, TResult> resultSelector)
{
var comp0 = comparer ?? Comparer<TKey>.Default;
var comp = descending ? Comparer<TKey>.Create((x, y) => -comp0.Compare(x, y)) : comp0;
var keys = source.Select(x => keySelector(x)).ToArray();
var indexes = Enumerable.Range(0, keys.Length).ToArray();
Array.Sort<TKey, int>(keys, indexes, comp);
var groups = new int[keys.Length];
int group = 0;
for (int j = 1; j < keys.Length; ++j)
{
if (comp.Compare(keys[j], keys[j - 1]) != 0)
++group;
groups[indexes[j]] = group;
}
int index = 0;
foreach (var item in source)
{
yield return resultSelector(item, groups[index++] + 1);
}
}