本机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; }