使用LINQ压缩事件列表

时间:2014-02-06 11:42:10

标签: c# linq

我有一个审计类

中的日志条目列表
public class Audit
{
    public DateTime TimeStamp { get; set; }
    public string User { get; set; }
    public string AuditType { get; set; }
}

所以列表可能如下所示;

20140206 11:29:20   Owen   Open   
20140206 11:29:21   Owen   Close
20140206 11:31:20   Owen   Open
20140206 11:32:20   Owen   Close
20140206 11:42:20   Owen   Open
20140206 11:50:00   Owen   Acknowledge

这给我们1秒,1分40秒的差距。因此,开放的最长时间是中间对,持续1分钟,然后在11:50确认。我正在寻找开放空间的日期对,在这种情况下是1分钟。

我知道我可以按顺序处理列表并使用TimeSpan找到最大的差距,但我认为有一种简洁的LINQ方式可以用组来做它吗?

UPDATE 这不是很漂亮,但这是真正扩展步行的逻辑

var audits = notice.AuditEntries.Where(a => a.User == user);

DateTime? currentOpen = null;
DateTime? bestOpen = null;
DateTime? bestClose = null;
foreach (var audit in audits)
{
    if (audit.AuditType == "Open")
    {
       if (currentOpen.HasValue) continue;
       currentOpen = audit.TimeStamp;
    }
    if (audit.AuditType == "Close" || audit.AuditType == "Acknowledge")
    {
       if (currentOpen.HasValue)
       {
          DateTime? currentClose = audit.TimeStamp;
          if (!bestOpen.HasValue)
          {
             bestOpen = currentOpen;
             bestClose = currentClose;
          }
          else
          {
             if (bestClose.Value.Subtract(bestOpen.Value) >                                               currentClose.Value.Subtract(currentOpen.Value))
             {
                bestOpen = currentOpen;
                bestClose = currentClose;
             }
           }
         currentOpen = null;
         }
      }
  }

2 个答案:

答案 0 :(得分:2)

未经测试但应该有效:

var auditGaps = audits
    .GroupBy(a => a.User)
    .Select(g => new
    {
        User = g.Key,
        MinOpen = g.Where(a => a.AuditType == "Open").Select(a=> a.TimeStamp).Min(),
        MaxClosed = g.Where(a => a.AuditType == "Close").Select(a=> a.TimeStamp).Max(),
        MaxAcknowledge = g.Where(a => a.AuditType == "Acknowledge").Select(a=> a.TimeStamp).Max()
    })
    .Select(x => new
    {
        x.User,
        LargestOpenCloseGap = x.MaxClosed - x.MinOpen,
        LargestOpenAcknowledgeGap = x.MaxAcknowledge - x.MinOpen
    });

答案 1 :(得分:2)

我认为这会解决问题:

IEnumerable<Audit> audits = ...

var longestAuditsByUser = audits.OrderBy(a => a.Timestamp)
    // group by user, since presumably we don't want to match an open from one user with a close from another
    .GroupBy(a => a.User)
    .Select(userAudits => 
    {
        // first, align each audit entry with it's index within the entries for the user
        var indexedAudits = userAudits.Select((audit, index) => new { audit, index });
        // create separate sequences for open and close/ack entries
        var starts = indexedAudits.Where(t => t.audit.AuditType == "Open");
        var ends = indexedAudits.Where(t => t.audit.AuditType == "Close" || t.audit.AuditType == "Acknowledge");

        // find the "transactions" by joining starts to ends where start.index = end.index - 1
        var pairings = starts.Join(ends, s => s.index, e => e.index - 1, (start, end) => new { start, end });

        // find the longest such pairing with Max(). This will throw if no pairings were
        // found. If that can happen, consider changing this Select() to SelectMany()
        // and returning pairings.OrderByDescending(time).Take(1)
        var longestPairingTime = pairings.Max(t => t.end.Timestamp - t.start.Timestamp);
        return new { user = userAudits.Key, time = longestPairingTime };
    });

// now that we've found the longest time for each user, we can easily find the longest
// overall time as well
var longestOverall = longestAuditsByUser.Max(t => t.time);