我如何使用LINQ到从每个组中选择最值
当我有一个代码段,如:
var teams = new Team[]
{
new Team{PlayerName="Ricky",TeamName="Australia", PlayerScore=234},
new Team{PlayerName="Hussy",TeamName="Australia", PlayerScore=134},
new Team{PlayerName="Clark",TeamName="Australia", PlayerScore=334},
new Team{PlayerName="Sankakara",TeamName="SriLanka", PlayerScore=34},
new Team{PlayerName="Udana",TeamName="SriLanka", PlayerScore=56},
new Team{PlayerName="Jayasurya",TeamName="SriLanka", PlayerScore=433},
new Team{PlayerName="Flintop",TeamName="England", PlayerScore=111},
new Team{PlayerName="Hamirson",TeamName="England", PlayerScore=13},
new Team{PlayerName="Colingwood",TeamName="England", PlayerScore=421}
};
期望的结果:
Team Name Player Name Score
Srilanka Jayasurya 433
England colingwood 421
Australia Clark 334
我的答案是类似尤里的,但使用MaxBy
从MoreLINQ ,不需要比较受整数来完成:
var query = from player in players
group player by player.TeamName into team
select team.MaxBy(p => p.PlayerScore);
foreach (Player player in query)
{
Console.WriteLine("{0}: {1} ({2})",
player.TeamName,
player.PlayerName,
player.PlayerScore);
}
请注意,我从“团队”,以“玩家”,因为我相信它更有意义改变了类型的名字 - 你不与球队集合开始,你的队员集合出发。
以下代码获取所需的值:
foreach (Team team in teams
.GroupBy(t => t.TeamName)
.Select(ig => ig.MaxValue(t => t.PlayerScore)))
{
Console.WriteLine(team.TeamName + " " +
team.PlayerName + " " +
team.PlayerScore);
}
它需要以下扩展名,我今天早些时候写道:
public static T MaxValue<T>(this IEnumerable<T> e, Func<T, int> f)
{
if (e == null) throw new ArgumentException();
using(var en = e.GetEnumerator())
{
if (!en.MoveNext()) throw new ArgumentException();
int max = f(en.Current);
T maxValue = en.Current;
int possible = int.MaxValue;
while (en.MoveNext())
{
possible = f(en.Current);
if (max < possible)
{
max = possible;
maxValue = en.Current;
}
}
return maxValue;
}
}
下面获取不带扩展名的答案,但稍微慢一点:
foreach (Team team in teams
.GroupBy(t => t.TeamName)
.Select(ig => ig.OrderByDescending(t => t.PlayerScore).First()))
{
Console.WriteLine(team.TeamName + " " +
team.PlayerName + " " +
team.PlayerScore);
}
这将要求你按团队名称,然后选择最高分。
唯一棘手的部分是获得相应的播放器,但它不是太糟糕了。 只需选择与最高分球员。 粗的,如果它的可能的一个以上的玩家,以如下所示,而不是单()函数具有相同的分数不使用First()函数此。
var x =
from t in teams
group t by t.TeamName into groupedT
select new
{
TeamName = groupedT.Key,
MaxScore = groupedT.Max(gt => gt.PlayerScore),
MaxPlayer = groupedT.First(gt2 => gt2.PlayerScore ==
groupedT.Max(gt => gt.PlayerScore)).PlayerName
};
仅供参考 - 我没跑对你的数据的代码,它的工作(后我固定一个,小的数据错误)。
我会用这个Lambda表达式:
IEnumerable<Team> topsScores =
teams.GroupBy(x => x.TeamName).Select(t => t.OrderByDescending(c => c.PlayerScore).FirstOrDefault());
通过该跛脚鸭提出的实现是伟大的,但需要两个O(N)经过分组集找出最大。 这将有利于从一次计算MaxScore,然后再利用。 这是的SelectMany(C#中的let关键字)就派上用场了。 下面是优化查询:
var x = from t in teams
group t by t.TeamName into groupedT
let maxScore = groupedT.Max(gt => gt.PlayerScore)
select new
{
TeamName = groupedT.Key,
MaxScore = maxScore,
MaxPlayer = groupedT.First(gt2 => gt2.PlayerScore == maxScore).PlayerName
};
我建议你首先实现对所谓的顶部例如,IEnumerbale类的扩展方法:
IEnumerable<T,T1> Top(this IEnumerable<T> target, Func<T1> keySelector, int topCount)
{
return target.OrderBy(i => keySelector(i)).Take(topCount);
}
然后,你可以这样写:
teams.GroupBy(团队=> team.TeamName)赛拓朴(团队=> team.PlayerScore,1)。
可能有一些轻微的修改,使其编译。