Groupby DateTime用于特定时间间隔

时间:2016-08-11 08:27:44

标签: c# linq

我有以下类的对象列表:

public class CounterData 
{
    public DateTime counterTime { get; set; }
    public int counterName { get; set; }
    public int count { get; set; }
}

例如,我有{counterTime, counterName, count}格式的以下数据列表:

{"Aug  8 2016  9:00AM","counter1",11}
{"Aug  8 2016  9:05AM","counter2",12}
{"Aug  8 2016  9:11AM","counter3",47}
{"Aug  8 2016  9:12AM","counter3",20}
{"Aug  8 2016  9:13AM","counter1",12}
{"Aug  8 2016  9:30AM","counter3",61}
{"Aug  8 2016  9:35AM","counter2",35}
{"Aug  8 2016  9:39AM","counter1",16}
{"Aug  8 2016  9:40AM","counter1",92}
{"Aug  8 2016  9:53AM","counter2",19}

我希望按每隔15分钟的时间聚合counterNamecounterTime对计数器进行分组。对于上面的示例,结果列表应为:

{"Aug  8 2016  9:00AM","counter1",23}
{"Aug  8 2016  9:00AM","counter2",12}
{"Aug  8 2016  9:00AM","counter3",67}
{"Aug  8 2016  9:30AM","counter3",61}
{"Aug  8 2016  9:30AM","counter2",35}
{"Aug  8 2016  9:30AM","counter1",108}
{"Aug  8 2016  9:45AM","counter2",19}

从上午9点到9点15分,共有2个counter1条目。因此count值是条目的总和。其他柜台也是如此。

我们可以使用LINQ GroupBy来解决这个问题吗?如果是,那怎么样?

2 个答案:

答案 0 :(得分:4)

这是一个将时间舍入到最接近的15分钟的解决方案。它使用AddSeconds来基本上摆脱DateTime的秒部分,然后使用AddMinutes var data = counters .GroupBy(cd => new { Date = cd.counterTime.AddSeconds(-cd.counterTime.Second) .AddMinutes(-cd.counterTime.Minute % 15), CounterName = cd.counterName }) .Select(g => new { Date = g.Key.Date, CounterName = g.Key.CounterName, SumCount = g.Sum(cd => cd.count) }); 来缩小分钟:

DbFunctions

但是,这不适用于LINQ to Entities(即实体框架),因此您需要稍微调整它以使用var data = counters .GroupBy(cd => new { Date = DbFunctions.AddMinutes( DbFunctions.AddSeconds(cd.counterTime, -cd.counterTime.Second), -cd.counterTime.Minute % 15), CounterName = cd.counterName }) .Select(g => new { Date = g.Key.Date, CounterName = g.Key.CounterName, SumCount = g.Sum(cd => cd.count) }); 方法:

"resources": [
{
  "type": "Microsoft.Search/searchServices",
  "name": "[parameters('serviceName')]",
  "apiVersion": "[parameters('serviceApiVersion')]",
  "location": "[parameters('location')]",
  "properties": {
    "sku": {
      "name": "[parameters('sku')]"
    },
    "replicaCount": 1,
    "partitionCount": 1,
    "hostingMode": "[parameters('hostingMode')]"
  }
},
{
  "type": "Microsoft.Search/searchServices",
  "name": "[concat(parameters('serviceName'), 'secondary')]",
  "apiVersion": "[parameters('serviceApiVersion')]",
  "location": "[parameters('location')]",
  "properties": {
    "sku": {
      "name": "[parameters('sku')]"
    },
    "replicaCount": 1,
    "partitionCount": 1,
    "hostingMode": "[parameters('hostingMode')]"
  }
}]

答案 1 :(得分:1)

以下是将日期分组15分钟的示例。它可能效率不高,并且不适用于EF。但只要你只对LINQ做对象就应该没问题。

借助this question的扩展程序:

public static class DateTimeExtensions
{
    public static DateTime RoundDown(this DateTime dt, TimeSpan d)
    {
        return new DateTime(((dt.Ticks + 1) / d.Ticks) * d.Ticks);
    }
}

这就是分组:

var counter = new List<CounterData>
{
    new CounterData {counterTime = DateTime.Now.AddMinutes(10), counterName = "counter1" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(15), counterName = "counter2" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(20), counterName = "counter3" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(25), counterName = "counter3" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(30), counterName = "counter1" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(35), counterName = "counter3" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(40), counterName = "counter2" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(45), counterName = "counter1" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(50), counterName = "counter1" },
    new CounterData {counterTime = DateTime.Now.AddMinutes(55), counterName = "counter2" },
};

// Now it's grouped by date.
var groupedCounters = counter.GroupBy(x => x.counterTime.RoundDown(TimeSpan.FromMinutes(15))).Select(d => new { Date = d.Key, Counters = d.ToList() });
List<CounterData> result = new List<CounterData>();

// With foreach.
foreach (var groupedCounter in groupedCounters)
{
    // Now we group by name as well.
    var countersByName =
        groupedCounter.Counters.GroupBy(x => x.counterName)
            .Select(
                x =>
                    new CounterData
                    {
                        count = x.Sum(item => item.Count),
                        counterTime = groupedCounter.Date,
                        counterName = x.Key
                    });
    result.AddRange(countersByName);
}

// Or with LINQ.

foreach (var countersByName in groupedCounters.Select(groupedCounter => groupedCounter.Counters.GroupBy(x => x.counterName)
    .Select(
        x =>
            new CounterData
            {
                count = x.Sum(item => item.Count),
                counterTime = groupedCounter.Date,
                counterName = x.Key
            })))
{
    result.AddRange(countersByName);
}