Linq - 每组的最高价值

时间:2009-10-15 21:52:22

标签: c# linq lambda

如何使用Linq从每个组中选择Top值

当我有一个代码段,如:

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 

5 个答案:

答案 0 :(得分:28)

我的回答类似于Yuriy,但使用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);
}

请注意,我已经将类型名称从“团队”更改为“玩家”,因为我认为它更有意义 - 您不是从一组团队开始,而是从一组玩家开始。< / p>

答案 1 :(得分:26)

以下代码获取所需的值:

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);
}

答案 2 :(得分:13)

这将要求您按团队名称分组,然后选择最高分数。

唯一棘手的部分是获得相应的玩家,但它并不太糟糕。只需选择具有最高分数的玩家。粗略的,如果多个玩家可以使用相同的分数,可以使用如下所示的First()函数而不是Single()函数。

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
    };

仅供参考 - 我确实针对你的数据运行了这个代码并且它有效(在我修复了那个,很少有数据错误之后)。

答案 3 :(得分:7)

我会使用这个Lambda表达式:

IEnumerable<Team> topsScores = 
teams.GroupBy(x => x.TeamName).Select(t => t.OrderByDescending(c => c.PlayerScore).FirstOrDefault());

答案 4 :(得分:-1)

我建议你首先在名为Top的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 =&gt; team.TeamName).Top(team =&gt; team.PlayerScore,1)。

可能会进行一些细微的修改以使其编译。