简体   繁体   中英

Iterating through list by DateTime property

I have a List and I need to calculate average of Value for each day by Created. How can I elegantly iterate the list for each day presented in the list? There can be gaps in the days and varying number of objects for each day.

For example values could be something like these:

public class MyObject
{
    public double Value { get; set; }
    public DateTime Created { get; set; }
}

{ Value: 1, Created: 2018-07-01 12:00:00 }
{ Value: 4, Created: 2018-07-01 20:00:00 }
{ Value: 2, Created: 2018-07-03 12:00:00 }
{ Value: 3, Created: 2018-07-04 09:00:00 }
{ Value: 2, Created: 2018-07-04 11:00:00 }
{ Value: 6, Created: 2018-07-04 12:00:00 }
{ Value: 5, Created: 2018-07-06 16:00:00 }
{ Value: 3, Created: 2018-07-06 11:00:00 }

I could go for something like this...

List<MyObject> items = itemsfrombackend;
DateTime beginDay = items.First().Created;
DateTime endDay = items.Last().Created;
for (DateTime day = beginDay; day <= endDay; day.AddDays(1))
{
    // if items contains any matching objects calculate avg
}

But would like to find a simpler way with less ifs

I would use a GroupBy() combined with Average()

var result = items.GroupBy(x => x.Created.Date)
                  .Select(x => new { Created = x.Key, Avg = x.Average(y => y.Value) });

You can use LINQ to group the data and calculate averages. You can use DateTime.Date to retrieve the date part of a DateTime value,eg :

var averages=items.GroupBy(grp=>grp.Created.Date)
        .Select(grp=>new {Date=grp.Key,Average=grp.Average(it=>it.Value)});

Or in query form:

var averages=from item in items
             group item by item.Created.Date into grp
             select new { Date=grp.Key,
                          Average=grp.Average(it=>it.Value)
                        };

GroupBy creates a "bucket" of items that share the same key. That bucket can be treated as a collection, allowing you to filter it, pick individual items or calculate aggregate functions based on its contents, like Averabe() .

Many of the One of the Average() overloads allow you to specify the property you want to use for the calculation.

grp.Average(it=>it.Value)

Selectes and averages the contents of the Value of the items found in each group's bucket

You can solve it completely using LINQ as others mentioned. You can try this method too, which has some LINQ ;-)

Dictionary<DateTime, decimal> avg = new Dictionary<DateTime, decimal>();

foreach (MyObject obj in items)
{
    if (avg.ContainsKey(obj.Created.Date))
    {
        avg[obj.Created.Date] += (decimal)obj.Value;
    }
    else
    {
        avg.Add(obj.Created.Date, (decimal)obj.Value);
    }
}

foreach (var item in avg.ToList())
{
    avg[item.Key] =Math.Round(item.Value / items.Count(x => x.Created.Date == Convert.ToDateTime(item.Key).Date),1);
}

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