Bad Dub
Bad Dub

Reputation: 1593

Linq Group By Object Then Select

I'm looking to group records by their LeagueId and Date. Then order desc on the date and then select the first occurrence of a distinct LeagueId. So far I have something like this but I don't know how to select stats in the first occurrence of a distinct LeagueId.

var stats =
    _statsRepository.FindBy(x => x.Active)
         .GroupBy(x => new { x.LeagueId, x.Date })
         .OrderByDescending(x => x.Key.Date)
         .SelectMany(x => x);

Its pretty much a way to get the most recent stats per league. So League 1 = 12/02/2017 and league 2 could be 02/02/17. <--- disregard this, may be misleading.

There are many stat records that occur on a date in a league. So for a certain date with each league there would be multiple stats.

Record 1:
09/01/14,
Jim,
4 Goals,
League 1

Record 2:
13/01/14,
Jack,
2 Goals,
League 1

Record 3:
13/01/14,
James,
2 Goals,
League 1

Record 4:
15/01/14,
Hannah,
2 Goals,
League 2

Record 5:
15/01/14,
Harmony,
1 Goal,
League 2

Record 6:
10/01/14,
Alision,
3 Goals,
League 2

The records that should be selected are

Record 2:
13/01/14,
Jack,
2 Goals,
League 1

Record 3:
13/01/14,
James,
2 Goals,
League 1

Record 4:
15/01/14,
Hannah,
2 Goals,
League 2

Record 5:
15/01/14,
Harmony,
1 Goal,
League 2

Explanation: For league 1 Records 2 & 3 occurs later than record 1 so they are both selected. For league 2 records 4 & 5 should be selected as they are the latest date.

Upvotes: 2

Views: 10411

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205849

With the latest update, you need to group by LeagueId and simply get all the records from each grouping with Date equals max Date in the grouping:

var stats = _statsRepository.FindBy(x => x.Active)
    .GroupBy(x => x.LeagueId)  
    .SelectMany(g => g.Where(x => x.Date == g.Max(y => y.Date));

Another way to get the same result is to use !Any (SQL NOT EXISTS) construct:

var baseStats = _statsRepository.FindBy(x => x.Active);
var stats = baseStats
    .Where(x => !baseStats.Any(y => y.LeagueId == x.LeagueId && y.Date > x.Date));

I assume you work with IQueryables (i.e. not LINQ to Objects where such things matter), so no optimizations like intermediate Selects are needed.

Upvotes: 5

Nkosi
Nkosi

Reputation: 247551

You want to group by league then take the most recent date in the group for each league

var stats = _statsRepository.FindBy(x => x.Active)
            .GroupBy(x => new { x.LeagueId, x.Date })
            .GroupBy(g => g.Key.LeagueId)
            .Select(gg => new { LeagueId = gg.Key, Stats = gg.OrderByDescending(g => g.Key.Date).First().ToList() })
            .ToList();

Upvotes: 1

Related Questions