本地C#支持检查IEnumerable是否已排序?

时间:2013-11-05 09:55:56

标签: c# linq sorted sortedlist icomparable

是否有任何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;
}

5 个答案:

答案 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;
}