是否有任何LINQ支持来检查IEnumerable<T>
是否已排序?我有一个我想要验证的枚举按非降序排序,但我似乎无法在C#中找到它的本机支持。
我使用IComparables<T>
编写了自己的扩展方法:
public static bool IsSorted<T>(this IEnumerable<T> collection) where T : IComparable<T>
{
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<T>
对象的人:
public static bool IsSorted<T>(this IEnumerable<T> collection, IComparer<T> 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;
}
答案 0 :(得分:5)
您可以检查集合是否为IOrderedEnumerable
,但仅当排序是应用于序列的最后一个操作时才会起作用。所以,基本上你需要手动检查所有序列。
另请注意,如果序列为IOrderedEnumerable
,您实际上无法说出使用哪种条件对序列进行排序。
这是一个通用的方法,您可以使用它来检查序列是否按您要检查的字段按升序排序:
public static bool IsOrdered<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> keySelector)
{
if (source == null)
throw new ArgumentNullException("source");
var comparer = Comparer<TKey>.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
方法 - 只需将检查比较结果更改为comparer.Compare(current, next) < 0
。
答案 1 :(得分:1)
没有这样的内置支持。
显然,如果您的IEnumerable<T>
也实施了IOrderedEnumerable<T>
,那么您不需要进行额外的检查,否则您必须像以前那样实施扩展方法。
顺便说一句,您可能希望添加方向参数或将其名称更改为IsSortedAscending<T>
。此外,您的T
可能会有不同的属性进行排序,因此您必须以某种方式明白“排序”的含义。
答案 2 :(得分:0)
我经常发现我创建的名为SelectPairs()
的扩展方法的用法,在这种情况下也是如此:
/// <summary>
/// Projects two consecutive pair of items into tuples.
/// {1,2,3,4} -> {(1,2), (2,3), (3,4))
/// </summary>
public static IEnumerable<Tuple<T, T>> SelectPairs<T>(this IEnumerable<T> source)
{
return SelectPairs(source, (t1, t2) => new Tuple<T, T>(t1, t2));
}
/// <summary>
/// Projects two consecutive pair of items into a new form.
/// {1,2,3,4} -> {pairCreator(1,2), pairCreator(2,3), pairCreator(3,4))
/// </summary>
public static IEnumerable<TResult> SelectPairs<T, TResult>(
this IEnumerable<T> source, Func<T, T, TResult> 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<T>(
this IEnumerable<T> source, Func<T, T, int> comparer)
{
return source.SelectPairs().All(t => comparer(t.Item1, t.Item2) > 0);
}
bool isOrdered = myCollection
.IsOrdered((o1, o2) => o2.MyProperty - o1.MyProperty);
答案 3 :(得分:0)
使用Zip有一个简短的版本,尽管你的IEnumerable确实被枚举了两次。
var source = Enumerable.Range(1,100000);
bool isSorted = source.Zip(source.Skip(1),(a,b)=&gt; b&gt; = a).All(x =&gt; x);
答案 4 :(得分:-1)
这是一个使用谓词来选择要按顺序排列的值的实现。
public static bool IsOrdered<TKey, TValue>(this IEnumerable<TKey> list, Func<TKey, TValue> 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;
}