C# Linq - 每个组的最高值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1575316/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:58:56  来源:igfitidea点击:

Linq - Top value from each group

c#linqlambda

提问by user190560

How can I employ Linq to select Top value from each group

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

when I have a code segment like :

当我有一个代码段时:

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

Desired Result :

预期结果:


Team Name         Player Name     Score

Srilanka          Jayasurya        433

England           colingwood       421

Australia         Clark            334 

采纳答案by Jon Skeet

My answer is similar to Yuriy's, but using MaxByfrom MoreLINQ, which doesn't require the comparison to be done by ints:

我的答案类似于 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);
}

Note that I've changed the type name from "Team" to "Player" as I believe it makes more sense - you don't start off with a collection of teams, you start off with a collection of players.

请注意,我已将类型名称从“Team”更改为“Player”,因为我认为这更有意义 - 您不是从一组球队开始,而是从一组球员开始。

回答by Yuriy Faktorovich

The following code gets the desired value:

以下代码获取所需的值:

foreach (Team team in teams
    .GroupBy(t => t.TeamName)
    .Select(ig => ig.MaxValue(t => t.PlayerScore)))
{
    Console.WriteLine(team.TeamName + " " + 
        team.PlayerName + " " + 
        team.PlayerScore);
}

It requires the following extension that I wrote earlier today:

它需要我今天早些时候写的以下扩展:

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

The following gets the answer without the extension, but is slightly slower:

以下得到没有扩展名的答案,但速度稍慢:

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

回答by Vitaliy

I would suggest you first implement an extension method on the IEnumerbale class called Top For example:

我建议您首先在名为 Top 的 IEnumerbale 类上实现一个扩展方法,例如:

IEnumerable<T,T1> Top(this IEnumerable<T> target, Func<T1> keySelector, int topCount)
{
    return target.OrderBy(i => keySelector(i)).Take(topCount);
}

Then you can write:

然后你可以写:

teams.GroupBy(team => team.TeamName).Top(team => team.PlayerScore, 1).

team.GroupBy(team => team.TeamName).Top(team => team.PlayerScore, 1).

There might be some slight modifications to make it compile.

可能会有一些轻微的修改以使其编译。

回答by Michael La Voie

This will require you to group by team name then select the max score.

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

The only tricky part is getting the corresponding player, but its not too bad. Just select the player with the max score. Of coarse, if its possible for more than one player to have identical scores do this using the First() function as shown below rather than the Single() function.

唯一棘手的部分是获得相应的播放器,但还不错。只需选择得分最高的玩家即可。粗略地说,如果可能有多个玩家具有相同的分数,请使用如下所示的 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
    };

FYI - I did run this code against your data and it worked (after I fixed that one, little data mistake).

仅供参考 - 我确实针对您的数据运行了此代码并且它起作用了(在我修复了那个小数据错误之后)。

回答by Drew Marsh

The implementation proposed by The Lame Duck is great, but requires two O(n) passes over the grouped set to figure out the Max. It would benefit from calculating MaxScore once and then reusing. This is where SelectMany (the let keyword in C#) comes in handy. Here is the optimized query:

The Lame Duck 提出的实现很棒,但需要在分组集上进行两次 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 
        };

回答by chris castle

I would use this Lambda expression:

我会使用这个 Lambda 表达式:

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