【问题标题】:How can I group on a specific amount of rows using LinQ?如何使用 LinQ 对特定数量的行进行分组?
【发布时间】:2021-01-20 08:28:17
【问题描述】:

我正在创建一个分数跟踪网络应用程序供我和我的朋友使用。跟踪器必须根据回合数最少的玩家已经玩了多少回合来对分数进行分组。

示例:如果我打了 6 局,而我的一个朋友只打了 4 局,那么他的所有局都将计入锦标赛,并且只考虑我最好的 4 局。

这是我目前所拥有的:

[ActionName("List")]
public async Task<IActionResult> List()
{
    var allData = await _cosmosDbService.GetScoresAsync(
        "SELECT * FROM c");
    var countRecordData = allData.GroupBy(l => l.Player).Select(cl => new Score
    {
        Player = cl.Count().ToString()
    }).ToList();

    var minRecord = countRecordData.Min(record => record.Player);
    int minRecordInt = Int32.Parse(minRecord);

    var sumData = allData.GroupBy(l => l.Player).Select(cl => new Score
    {
        Player = cl.First().Player,
        Points = cl.Sum(c => c.Points),
        Strokes = cl.Sum(c => c.Points),
    }).ToList();
        
    return View(sumData);
}

minRecord 仅用于获取记录数最少的玩家(最少数量)。我没有在其他任何地方使用它。

这是分数模型:

public class Score
{
    [JsonProperty(PropertyName = "id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "Player")]
    public string Player { get; set; }

    [JsonProperty(PropertyName = "Course")]
    public string Course { get; set; }

    [JsonProperty(PropertyName = "Points")]
    public int Points { get; set; }

    [JsonProperty(PropertyName = "Strokes")]
    public int Strokes { get; set; }
}

有谁知道我怎样才能达到这个要求?

【问题讨论】:

  • 还有你的 Player 类,示例输入和输出会有所帮助。
  • 你到底是在坚持什么?这读起来很像有人可以为我做这件事吗,有点问题,不会得到太多的爱
  • @A.Grefslie:我认为你的问题很好(也许不是很清楚,但我想我理解这个问题)。您很快就对源代码的问题做出了反应,我不认为这是一个坏问题。
  • 不知道如何根据回合数最少的玩家的回合数对分数进行分组。这意味着如果我打了 6 轮,而我的朋友打了 4 轮,那么他的所有轮都将计算在内,只有我最好的 4 轮将计算在内。所以排除了我最差的两轮。
  • 要将积分计算限制为 minRecordInt 匹配,请尝试将积分计算更改为 Points = cl.OrderByDescending(c=&gt;c.Points).Take(minRecordInt).Sum(c =&gt; c.Points)

标签: c# asp.net .net linq


【解决方案1】:

您已经接近实现您想要的目标。这是我对您正在尝试做的事情的看法。

如果数据量很小,那么多次使用GroupBy 并不重要,但最好只创建一次组:

var groupings = allData
    .GroupBy(
        score => score.Player,
        (player, scores) => new
        {
            Player = player,
            Scores = scores.ToList() 
        })
    .ToList();

调用ToList 将确保每个玩家的得分集合被枚举并分配到列表中。

我不确定您确定最小记录数的部分。计算Player 属性的最小值看起来很奇怪,我假设它是玩家的名字。

这里我计算所有玩家的最低分数:

var minScoreCount = groupings.Min(grouping => grouping.Scores.Count);

在最后一部分中,您必须使用Take(minScoreCount) 仅获取每个玩家的前 N ​​个分数。我认为这是您自己的代码中缺少的部分。

var sumData = groupings.Select(grouping => new Score
{
    Player = grouping.Player,
    Points = grouping.Scores
        .OrderByDescending(score => score.Points)
        .Take(minScoreCount)
        .Sum(score => score.Points),
    Strokes = grouping.Scores
        .OrderBy(score => score.Strokes)
        .Take(minScoreCount)
        .Sum(score => score.Strokes)
}).ToList();

如果您没有要求对 PointsStrokes 的分数进行不同的排序,您可以在 GroupBy 内的 ToList 之前对分数进行排序,以减少代码遍历列表的次数分数。

【讨论】:

  • 它分组正确,但我无法正确排序。由于某种原因,它在点和笔画中或多或少是随机顺序
  • @A.Grefslie:排序用于产生您在评论中定义的“最高”分数。那部分肯定不是随机的,并且在相同的输入下,它总是会产生相同的输出。但是,由于它似乎没有产生所需的输出,您需要更清楚地解释您的要求,并可能提供一个演示问题的示例。通过这样做,您甚至可能会发现如何修改我的答案以符合您的真实要求。
  • 对不起,我想错了。你是对的,你的解决方案完全符合要求!再次感谢您的帮助:)
【解决方案2】:

不确定它有多漂亮,但给你。你可以这样做:

[ActionName("List")]
public async Task<IActionResult> List()
{
    var allData = await _cosmosDbService.GetScoresAsync(
        "SELECT * FROM c");
    var playerRounds = allData.GroupBy(l => l.Player);
    var minRounds = playerRounds.Min(x => x.Count());

    var sumData = playerRounds.Select(cl => new Score
    {
        Player = cl.First().Player,
        Points = cl.OrderByDescending(x => x.Points).Take(minRounds).Sum(c => c.Points),
        Strokes = cl.OrderByDescending(x => x.Points).Take(minRounds).Sum(c => c.Points),
    }).ToList();

    return View(sumData);
}

https://dotnetfiddle.net/ho1psw

【讨论】:

    猜你喜欢
    • 2017-11-29
    • 2013-06-09
    • 2023-01-11
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    相关资源
    最近更新 更多