Linq - 每组的最高值

发布于 2024-08-07 17:50:11 字数 934 浏览 2 评论 0原文

如何使用 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 

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

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 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

盛夏尉蓝 2024-08-14 17:50:11

我的答案与 Yuriy 的类似,但使用 MaxBy 来自 MoreLINQ,这不需要比较由 ints 完成:

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

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

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

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.

放血 2024-08-14 17:50:11

以下代码获取所需的值:

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

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);
}
停顿的约定 2024-08-14 17:50:11

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

唯一棘手的部分是找到相应的球员,但这还不错。只需选择得分最高的玩家即可。当然,如果可能有多个玩家具有相同的分数,请使用如下所示的 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
    };

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

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.

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).

栖迟 2024-08-14 17:50:11

我会使用这个 Lambda 表达式:

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

I would use this Lambda expression:

IEnumerable<Team> topsScores = 
teams.GroupBy(x => x.TeamName).Select(t => t.OrderByDescending(c => c.PlayerScore).FirstOrDefault());
浮生面具三千个 2024-08-14 17:50:11

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

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:

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 
        };
陈甜 2024-08-14 17:50:11

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

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

那么你可以这样写:

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

可能需要进行一些细微的修改才能编译。

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

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).

There might be some slight modifications to make it compile.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文