How can I group on a specific amount of rows using LinQ?

Viewed 83

I am creating a score-tracking web application for my friends and I to use. The tracker have to group the scores based on how many rounds the player with the least amount of rounds have been playing.

Example: If I have played 6 rounds, and a friend of mine has only played 4, then all of his rounds would be counting in the tournament and only the 4 best of mine would be taken to account.

This is what I have so far:

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

the minRecord is only used to get the player with least amount of records(number of least amount). I am not using it anywhere else.

Here is the Score model:

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

Does anyone know how I can reach this requirement?

2 Answers

Not sure how pretty it is, but here you go. You can do it like this:

[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

You are close to achieve what you want. Here is my take on what I believe you are trying to do.

If the amount of data is small it doesn't matter that you are using GroupBy multiple times but it might be better to create the groups only once:

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

Calling ToList will ensure that the collection of scores for each player are enumerated and allocated into lists.

I'm not sure about the part where you determine the minimum number of records. It looks odd to compute the minimum of the Player property which I assume is the name of the player.

Here I compute the minimum number of scores for all players:

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

In the last part you have to use Take(minScoreCount) to only take the first N scores for each player. I think this is the part that you are missing in your own code.

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

If you didn't have the requirement to sort the scores differently for Points and Strokes you could sort the scores before the ToList inside the GroupBy reducing the number of times the code is traversing the lists of scores.

Related