2014-02-06 57 views
1

我在審計類的日誌條目列表凝結事件列表與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; 
     } 
     } 
    } 
+0

這取決於您的數據是什麼樣子。例如,您是否確保爲單個用戶交換序列的Open和Close/Acknoledge(當按時間戳排序時)?這個序列是否總是以Open開頭?不同的用戶穿插?你想要每個用戶還是跨用戶的結果? – ChaseMedallion

+0

我不能保證序列會完美交替,所以我在順序步行中處理它。我只是簡化了我嘗試過的代碼,然後我會在 – owen79

回答

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); 
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 
    }); 
+0

之後發佈這個代碼,但我會試試這個,但首先感覺就像是有10個開啓和關閉的每個5秒,它看起來像是50秒打開和關閉。我會給它一個去 – owen79

+0

對不起,我不明白你的意思。我現在已經測試過了,它產生了一個user =「Owen」的元素(因爲你的樣本數據只包含一個)和這個'ToString()':'{User = Owen,LargestOpenCloseGap = 00:03:00,LargestOpenAcknowledgeGap = 00 :21:00}'。它出什麼問題了?也許我誤解了,因爲我只計算了最大的時間戳。 –

+0

我認爲這是我的錯誤解釋,我已經更新了這個問題。在上面的例子中,我正在尋找1分鐘長的中間兩項之間的差距 – owen79