5

是否有任何 LINQ 支持检查 anIEnumerable<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;
}
4

5 回答 5

6

您可以检查是否收集,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.

于 2013-11-05T10:06:22.530 回答
1

没有这样的内置支持。

显然,如果你IEnumerable<T>也实现IOrderedEnumerable<T>了,那么你不需要做额外的检查,否则你必须像你一样实现一个扩展方法。

顺便说一下,您可能想要添加一个方向参数或将其名称更改为IsSortedAscending<T>。此外,您可能需要对不同的属性T进行排序,因此您必须以某种方式清楚“排序”的含义。

于 2013-11-05T10:08:47.823 回答
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);
于 2014-04-17T06:00:02.977 回答
0

有一个使用 Zip 的简短版本,尽管您的 IEnumerable 确实被枚举了两次。

var source = Enumerable.Range(1,100000);

bool isSorted = source.Zip(source.Skip(1),(a,b)=>b>=a).All(x=>x);

于 2014-04-17T18:57:03.310 回答
-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;
}
于 2018-05-24T17:37:33.610 回答