LINQ:按时间分组的事件

时间:2012-01-24 04:28:58

标签: c# linq grouping

此代码搜索一起发生的事件组。 它们之间最多5秒。组之间的时间超过5秒。

更新:组是日期时间列表。一组包含DateTimes,其间小于5秒。发生在下一组的日期时间超过5秒。

public static List<List<DateTime>> GetGroups(int count)
{
  var groups = new List<List<DateTime>>();
  groups.Add(new List<DateTime>());

  using (var db = new DbContainer())
  {
    foreach (var row in db.Table)
    {
      if (!groups.Last().Any() || (groups.Last().Any() && (row.Time - groups.Last().Last()).TotalSeconds <= 5))
      {
        groups.Last().Add(row.Time);
      }
      else if (groups.Count < count)
      {
        groups.Add(new List<DateTime>());
        groups.Last().Add(row.Time);
        continue;
      }

      if (groups.Count == count)
      {
        break;
      }
    }
  }

  return groups;
}

我可以用一个或两个表达式在LINQ中实现相同的算法吗?

2 个答案:

答案 0 :(得分:2)

基本上,使用标准LINQ to Objects运算符难以表达的查询中唯一棘手的部分是根据连续彼此之间的距离对项目进行分组。

仅此一点,我会使用迭代器块:

// Needs argument-checking, but you'll need another method to do it eagerly.
public static IEnumerable<List<T>> GroupByConsective<T>
      (this IEnumerable<T> source, Func<T, T, bool> prevNextPredicate)
{
    var currentGroup = new List<T>();

    foreach (var item in source)
    {
        if (!currentGroup.Any() || prevNextPredicate(currentGroup.Last(), item))
            currentGroup.Add(item); // Append: empty group or nearby elements.
        else
        {
            // The group is done: yield it out
            // and create a fresh group with the item.
            yield return currentGroup;
            currentGroup = new List<T> { item };
        }
    }

   // If the group still has items once the source is fully consumed,
   // we need to yield it out.
   if(currentGroup.Any())
     yield return currentGroup;
}

对于其他所有内容(投影,限制组的数量,实现集合),标准LINQ to Objects将正常工作。所以你的查询变成了:

using (var db = new DbContainer())
{
   var groups = db.Table
                  .Select(row => row.Time)
                  .GroupByConsecutive((prev, next) => next.Subtract(prev)
                                                          .TotalSeconds <= 5)
                  .Take(count)
                  .ToList();

  // Use groups...

}

答案 1 :(得分:1)

.GroupBy(obj => long.Parse(obj.time.ToString("yyyyMMddHHmmss")) /5 )

使用datetime.ToString(),格式为每秒的基数,然后/ 5每5秒

编辑: 我不太确定你在看什么,但我试过这个并且它有效

var now = DateTime.Now;

            Console.WriteLine(now.ToString("yyyyMMddHHmmss"));

            Enumerable.Range(0, 57)
                .Select(offset => now.AddSeconds(offset))
                .GroupBy(interval => long.Parse(interval.ToString("yyyyMMddHHmmss")) / 5)
                .ToList()
                .ForEach(g => Console.WriteLine("{0}: {1} - {2}", g.Count(), g.Min().ToString("yyyyMMddHHmmss"), g.Max().ToString("yyyyMMddHHmmss")));

            Console.ReadKey();

以下是示例输出

20120125144606
4: 20120125144606 - 20120125144609
5: 20120125144610 - 20120125144614
5: 20120125144615 - 20120125144619
5: 20120125144620 - 20120125144624
5: 20120125144625 - 20120125144629
5: 20120125144630 - 20120125144634
5: 20120125144635 - 20120125144639
5: 20120125144640 - 20120125144644
5: 20120125144645 - 20120125144649
5: 20120125144650 - 20120125144654
5: 20120125144655 - 20120125144659
3: 20120125144700 - 20120125144702

采样日期时间以5秒的间隔进行分组。例如,从10-14开始的第二次。如果你想要11 - 15你可以在继续前加1秒 :)