我在C#中有一个列表,该列表具有两个datetime字段:DatePaid和DateEntered。我想查找乱序元素。这很容易,老派。按DatePaid对列表进行排序,然后遍历列表,将DateEntered捕获到局部变量(lastDateEntered)中。每次循环时,我们首先比较DateEntered和lastDateEntered。如果DateEntered小于lastDateEntered,则上一行将是乱序行。
| DatePaid | DateEntered | comments |
|----------|-------------|--------------|
| 1/1/2019 | 1/1/2019 | |
| 2/1/2019 | 2/2/2019 | |
| 3/1/2019 | 3/1/2019 | |
| 4/1/2019 | 5/2/2019 | out of order |
| 5/1/2019 | 5/1/2019 | |
在SQL中(如果您不关心SQL,请忽略此段),创建两个CTE's(就像子查询)很容易:一个按DatePaid排序,另一个按DateEntered排序。我们使用Row_Number()函数添加已对CTE排序的额外字段。然后,我们加入每个CTE的行号,然后仅选择日期从一个CTE到另一个CTE不相等的行。与Oracle 12c - sql to find out of order rows相似,尽管答案没有使用CTE。我想我可以通过linq来做些类似的事情,但我不确定它会比foreach循环方法容易。
有没有更好的Linqish方法?
最佳答案
使用基于APL扫描运算符的LINQ扩展(如Aggregate
,仅返回中间结果),该扩展将列表的prev和cur元素组合在一起以获得新值,解决方案很简单。
一,扩展方法:
// TRes combineFn(T prevValue, T curValue)
public static IEnumerable<TRes> ScanByPairs<T, TRes>(this IEnumerable<T> src, Func<T, T, TRes> combineFn) {
using (var srce = src.GetEnumerator())
if (srce.MoveNext()) {
var prev = srce.Current;
while (srce.MoveNext())
yield return combineFn(prev, prev = srce.Current);
}
}
现在您可以测试每个日期字段:
var ansdp = list.ScanByPairs((prev, cur) => new { OrderNotOkay = prev.DatePaid >= cur.DatePaid, prev })
.Where(op => op.OrderNotOkay)
.Select(op => op.prev)
.ToList();
var ansde = list.ScanByPairs((prev, cur) => new { OrderNotOkay = prev.DateEntered >= cur.DateEntered, prev })
.Where(op => op.OrderNotOkay)
.Select(op => op.prev)
.ToList();
(这给了我写
WhereByPairs
(及其许多同伴)的明显想法。)如果您不想使用扩展方法,则可以使用LINQ
Zip
方法来模拟同一件事:var ansde2 = list.Zip(list.Skip(1), (prev, cur) => new { OrderNotOkay = prev.DateEntered >= cur.DateEntered, prev })
.Where(op => op.OrderNotOkay)
.Select(op => op.prev)
.ToList();
而且,当然,您可以将订单测试封装在扩展方法中:
public static class ListDateExt {
public static IEnumerable<T> OutOfOrder<T, TField>(this IEnumerable<T> src, Func<T,TField> selectorFn, Comparer<TField> cmp = null) {
cmp = cmp ?? Comparer<TField>.Default;
return src.ScanByPairs((prev, cur) => new { OrderNotOkay = cmp.Compare(selectorFn(prev), selectorFn(cur)) >= 0, prev })
.Where(op => op.OrderNotOkay)
.Select(op => op.prev);
}
}
然后,您可以使用以下命令测试字段:
var ansdp = list.OutOfOrder(l => l.DatePaid).ToList();
var ansde = list.OutOfOrder(l => l.DateEntered).ToList();
关于c# - 如何在C#中使用LINQ查找乱序元素,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55872526/