简体   繁体   中英

How can I get the maximum value by hour in a list of object using Linq

I have a list of custom object that have values and datetimes. If is possible to do How can I filter the object with maximum values by hours.

So grouping the data by date and hours check the objects and get only one by hour witch has the higher value.

For instance I have

class CustomObj
{
  public int Id;
  public int Value;
  public DateTime Date;

}

List<CustomObj> listObj = new List<CustomObj>()

listObj.Add(....100 elemets..)

Id     Value    Date
1      4        2014-02-06 11:41:29.507
2      3        2014-02-06 12:11:29.510
3      4        2014-02-06 12:41:29.510
4      0        2014-02-06 13:11:29.510
5      2        2014-02-06 13:41:29.510
6      0        2014-02-06 14:11:29.510

So I am trying to get using linq a list that containing the items with Ids (1,3,5,6)

Ids 1,3,5,6 means that I should have a list with the object that from the data of the table above. list of custom objects as a result in a table we look like this Id Value Date 1 4 2014-02-06 11:41:29.507 3 4 2014-02-06 12:41:29.510 5 2 2014-02-06 13:41:29.510 6 0 2014-02-06 14:11:29.510

Getting items with max hour:

var objectsWithMaxHour = listObj.GroupBy(o => o.Date.Hour)
                                .OrderByDescending(g => g.Key)
                                .Take(1)
                                .SelectMany(g => g);

Group objects by hour of date. Then sort groups by value of grouping key (ie by hour) and take first group. Last step is flattening group items.

NOTE: This query will return single object with id 6 (because it's the only object with hour equal to 14).

If I am not wrong and you want to:

  • group by everything down to an hour (without minutes ans seconds),
  • order each group by Date
  • select Id of last element in each group

then this should work

var ids = listObj
           .GroupBy(n => new DateTime(n.Date.Year, n.Date.Month, n.Date.Day, n.Date.Hour, 0, 0))
           .Select(g => g.OrderBy(n => n.Date).Last().Id);

To get the object with the maximum Value for each Date.Hour in the dataset, we'll first want to GroupBy Date.Hour , then for each group select the object whose Value is equal to the maximum Value in that group, then finally flatten the Date.Hour grouping to get a list of objects whose Value is the highest among all other objects with the same Date.Hour . In code, this looks like:

var result = list.GroupBy(element => element.Date.Hour)
                 .SelectMany(group => group.Where(element => element.Value == group.Max(obj => obj.Value)));

When run on your data, initialized like:

        var list = new List<CustomObj>
                       {
                           new CustomObj {Date = DateTime.Parse("2014-02-06 11:41:29.507"), Id = 1, Value = 4},
                           new CustomObj {Date = DateTime.Parse("2014-02-06 12:11:29.510"), Id = 2, Value = 3},
                           new CustomObj {Date = DateTime.Parse("2014-02-06 12:41:29.510"), Id = 3, Value = 4},
                           new CustomObj {Date = DateTime.Parse("2014-02-06 13:11:29.510"), Id = 4, Value = 0},
                           new CustomObj {Date = DateTime.Parse("2014-02-06 13:41:29.510"), Id = 5, Value = 2},
                           new CustomObj {Date = DateTime.Parse("2014-02-06 14:11:29.510"), Id = 6, Value = 0},
                       }; 

The query returns:

Id    Value     Date
1     4         2/6/2014 11:41:29 AM
3     4         2/6/2014 12:41:29 PM
5     2         2/6/2014 1:41:29 PM
6     0         2/6/2014 2:11:29 PM

By combining @Sergey Berezovskiy 's and mine, you get a solution much cleaner than my original solution (since mine must iterate over the grouping twice). Simply group the elements by hour like before and SelectMany from the groupings after ordering the group members by Value and taking one. In code, this is:

var result = list.GroupBy(element => element.Date.Hour)
                 .SelectMany(group => group.OrderByDescending(g => g.Key)
                                           .Take(1));

Which will give the same result while only requiring a single iteration over your collection.

All of your example data are from one day, so I'm not sure if you want to

  1. return a single item for each hour regardless of day
  2. return a single item for each combination of hour and day.

Here is code that will meet the second requirement:

var lastItemsPerHour = listObj
    .GroupBy(c => new { c.Date.Date, c.Date.Hour },
        (k, ec) => ec.OrderBy(c => c.Date).Last());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM