问题
I have a query that returns a list of events in a date range.
string EOOmessage = "";[enter image description here][2]
string eventText = "";
DateTime js = DateTime.Now;
DateTime je = DateTime.Now;
var itCompareDay = (from h in db.DailyGPSTables
where (h.EventDateTime >= startDate
&& h.EventDateTime <= endDate)
select h).ToList();
I want to check the time for each event to make sure its in the proper sequence. For example JE(Job End) cannot be before JS(Job Start). I have tried many ways but this is my latest. It checks for a matching JE tag correctly but it doesn't account for which day it is in.
int rowNumber = -1;
foreach (DailyGPSTable e in itCompareDay)
{
if (e.EventType == "JS")
{
js = e.EventDateTime.Value;
}
if (e.EventType == "JE")
{
je = e.EventDateTime.Value;
}
if (je < js)
{
EOOmessage = " On " + e.EventDateTime.Value.ToShortDateString() + " Job end is before Job Start " + eventText;
errorList.Add(EOOmessage);
errorListRow.Add(rowNumber);
}
rowNumber = rowNumber + 1;
}
Is there a way to check each day for out of sequence events and if found report them if not go to the next day?
回答1:
Using an extension method that scans sequentially and groups while a test is true (or false) named GroupByWhile based on my extension method for scanning by pairs:
public static class IEnumerableExt {
// TKey combineFn((TKey Key, T Value) PrevKeyItem, T curItem):
// PrevKeyItem.Key = Previous Key
// PrevKeyItem.Value = Previous Item
// curItem = Current Item
// returns new Key
public static IEnumerable<(TKey Key, T Value)> ScanPair<T, TKey>(this IEnumerable<T> src, TKey seedKey, Func<(TKey Key, T Value), T, TKey> combineFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prevkv = (seedKey, srce.Current);
while (srce.MoveNext()) {
yield return prevkv;
prevkv = (combineFn(prevkv, srce.Current), srce.Current);
}
yield return prevkv;
}
}
}
// bool testFn(T prevItem, T curItem)
// returns groups by sequential matching bool
public static IEnumerable<IGrouping<int, T>> GroupByWhile<T>(this IEnumerable<T> src, Func<T, T, bool> testFn) =>
src.ScanPair(1, (kvp, cur) => testFn(kvp.Value, cur) ? kvp.Key : kvp.Key + 1)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value);
}
You can select out the interesting event types (JS/JE) ordered by EventDateTime and then group by JS followed by JE and throw out matching pairs:
var itCompareDay = (from h in db.DailyGPSTables
where (h.EventDateTime >= startDate
&& h.EventDateTime <= endDate)
orderby h.EventDateTime
select h).ToList();
var errEvents = itCompareDay
.Select((ev, rowNum) => new { ev.EventType, ev.EventDateTime, rowNum })
.Where(cd => cd.EventType == "JS" || cd.EventType == "JE")
.GroupByWhile((pd, cd) => pd.EventType == "JS" && cd.EventType == "JE" && pd.EventDateTime.Date == cd.EventDateTime.Date)
.Where(cdg => cdg.Count() != 2)
.SelectMany(cdg => cdg.Select(cd => new { cd.rowNum, ErrMsg = cd.EventType == "JE" ? "JE without preceding JS" : "JS without following JE" }));
Note that rowNum is 0 based but you could add 1 in the first Select if desired.
来源:https://stackoverflow.com/questions/53230672/iterate-through-linq-query-results-by-day-to-check-conditions