3

In C# I have a list locationsList which contains fields location and date, location can appear multiple times with different dates.

If location does appear multiple times I want to only keep the one with the latest date. Is there any way to do this using LINQ rather than iterate through the list and compare the dates of each multiple-occurring location?

dymanoid
  • 14,771
  • 4
  • 36
  • 64
DarkW1nter
  • 2,933
  • 11
  • 67
  • 120
  • 6
    Please provide a [mcve] of your current code. – dymanoid Sep 14 '17 at 08:04
  • As dymanoid mentioned, we can't really answer this if there's no code sample. Look into **grouping** the data based on the fields that are important to you. – Flater Sep 14 '17 at 08:06

1 Answers1

3

I assume the field location is a string, and you can rebuild your Location object:

class Location {
    public string location { get; set; }
    public DateTime date { get; set; }
}

var distincts = locationsList 
         .GroupBy(location => location.location)
         .Select(grp => new Location {
             location = grp.Key, 
             date = grp.Max(loc => loc.date)
         });

If rebuilding your object is not an option, you can use the MaxBy extension method by MoreLINQ:

var distincts = locationsList 
         .GroupBy(location => location.location)
         .Select(grp => grp.MaxBy(loc => loc.date))

For simplification, here is a non generic version of the MaxBy method, applied to your case:

Location MaxByDate(List<Location> locations){
    Location maxLocation = null;
    var maxDate = DateTime.MinValue;
    foreach(var location in locations) {
        if (location.date > maxDate)
        {
            maxLocation = location;
            maxDate = location.date;
        }
    }
    return maxLocation ;
}

Edit

Another alternative proposed by Flater, slightly worst in performance due to the fact that you must loop twice in each group, one for retrieving the Max, the second loop for finding the match. But very readable and maintainable.

var distincts = locationsList 
     .GroupBy(location => location.location)
     .Select(grp => {
         var maxDate = grp.Max(location => location.date);
         return grp.First(location => location.date == maxDate);
     })
Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
  • 3
    It can be done without MoreLINQ too: `Select(group => group.First(x => x.Date == group.Max(y => y.Date))`. A bit more verbose, but possible. (note that I used `First` instead of `FirstOrDefault` because null should logically never be encountered after a `GroupBy`) – Flater Sep 14 '17 at 08:14
  • 2
    @Flater, that will compute `Max` on the group for every item in the original source. This will have terrible performance. If you want to go that route, compute the max before calling `First`. – Drew Noakes Sep 14 '17 at 08:20