本机C#支持检查IEnumerable是否已排序?
是否有任何LINQ支持检查IEnumerable
是否已排序? 我有一个我要validation的枚举是按非降序排序的,但我似乎无法在C#中找到它的本机支持。
我使用IComparables
编写了自己的扩展方法:
public static bool IsSorted(this IEnumerable collection) where T : IComparable { Contract.Requires(collection != null); using (var enumerator = collection.GetEnumerator()) { if (enumerator.MoveNext()) { var previous = enumerator.Current; while (enumerator.MoveNext()) { var current = enumerator.Current; if (previous.CompareTo(current) > 0) return false; previous = current; } } } return true; }
一个使用IComparer
对象:
public static bool IsSorted(this IEnumerable collection, IComparer comparer) { Contract.Requires(collection != null); using (var enumerator = collection.GetEnumerator()) { if (enumerator.MoveNext()) { var previous = enumerator.Current; while (enumerator.MoveNext()) { var current = enumerator.Current; if (comparer.Compare(previous, current) > 0) return false; previous = current; } } } return true; }
您可以检查集合是否为IOrderedEnumerable
但仅当排序是应用于序列的最后一个操作时才会起作用。 所以,基本上你需要手动检查所有序列。
还要记住,如果序列是IOrderedEnumerable
你真的不能说哪个条件用于排序序列。
以下是通用方法,您可以使用它来检查序列是否按要检查的字段按升序排序:
public static bool IsOrdered( this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); var comparer = Comparer.Default; using (var iterator = source.GetEnumerator()) { if (!iterator.MoveNext()) return true; TKey current = keySelector(iterator.Current); while (iterator.MoveNext()) { TKey next = keySelector(iterator.Current); if (comparer.Compare(current, next) > 0) return false; current = next; } } return true; }
用法:
string[] source = { "a", "ab", "c" }; bool isOrdered = source.IsOrdered(s => s.Length);
您可以创建类似的IsOrderedDescending
方法 – 只需将检查比较结果更改为IsOrderedDescending
comparer.Compare(current, next) < 0
。
没有这样的内置支持。
显然,如果你的IEnumerable
也实现了IOrderedEnumerable
那么你不需要做额外的检查,否则你必须像你一样实现扩展方法。
顺便说一句,您可能希望添加方向参数或将其名称更改为IsSortedAscending
。 此外,您的T
可能有不同的属性可以进行排序,因此必须以某种方式明白“排序”的含义。
有一个使用Zip的简短版本,尽管你的IEnumerable确实被枚举了两次。
var source = Enumerable.Range(1,100000);
bool isSorted = source.Zip(source.Skip(1),(a,b)=> b> = a).All(x => x);
我经常发现我创建的一个名为SelectPairs()
的扩展方法的用法,在这种情况下:
/// /// Projects two consecutive pair of items into tuples. /// {1,2,3,4} -> {(1,2), (2,3), (3,4)) /// public static IEnumerable> SelectPairs(this IEnumerable source) { return SelectPairs(source, (t1, t2) => new Tuple(t1, t2)); } /// /// Projects two consecutive pair of items into a new form. /// {1,2,3,4} -> {pairCreator(1,2), pairCreator(2,3), pairCreator(3,4)) /// public static IEnumerable SelectPairs( this IEnumerable source, Func pairCreator) { T lastItem = default(T); bool isFirst = true; foreach (T currentItem in source) { if (!isFirst) { yield return pairCreator(lastItem, currentItem); } isFirst = false; lastItem = currentItem; } }
像这样用它:
bool isOrdered = myCollection .SelectPairs() .All(t => t.Item1.MyProperty < t.Item2.MyProperty);
这个陈述当然可以放在另一个扩展方法中:
public static bool IsOrdered( this IEnumerable source, Func comparer) { return source.SelectPairs().All(t => comparer(t.Item1, t.Item2) > 0); } bool isOrdered = myCollection .IsOrdered((o1, o2) => o2.MyProperty - o1.MyProperty);
这是一个使用谓词来选择要排序的值的实现。
public static bool IsOrdered(this IEnumerable list, Func predicate) where TValue : IComparable { if (!list.Any()) return true; var previous = predicate(list.First()); foreach(var entry in list.Skip(1)) { var current = predicate(entry); if (previous.CompareTo(current) > 0) return false; previous = current; } return true; }