简体   繁体   English

如何检查清单 <T> 是否根据其属性排序(升序或降序)?

[英]How to check if a list of <T> has order (asc or desc) or not, according its properties?

To get the order (if any) I'm doing this: 为了获得订单(如果有),我正在这样做:

( <T> always has at least one property) <T>始终至少具有一个属性)

private bool GetOrder<T>(IQueryable<T> list, out string order) where T: class, new()
{
    var props = (new T().GetType().GetProperties().OrderBy(x => x.Name).ToArray());

    foreach (var pro in props)
    {
        // order ascending by each property
        var comparable = list.OrderBy(x => pro.GetValue(x, null)).AsQueryable();

        if (list.SequenceEqual(comparable))
        {
            order = pro.Name + " asc";
            return true;
        }
        else
        {
            // then descending
            comparable = list.OrderByDescending(x => pro.GetValue(x, null)).AsQueryable();

            if (list.SequenceEqual(comparable))
            {
                order = pro.Name + " desc";
                return true;
            }
        }
    }

    order = "none";
    return false;
}

Testing this I'm getting acceptable results, may be slow (4-5 secs for 500K elements) but some way works. 测试这个我得到可接受的结果,可能会很慢(500K元素需要4-5秒),但是有些方法可以工作。

Some issues come when order criteria may be more than one, and this method only get the first, is there any other (faster) way, I'm pretty sure there is, but I can find it or get it. 当订购标准可能不止一个时,就会出现一些问题,而这种方法只能得到第一种,还有其他(更快)的方法吗,我敢肯定有,但是我可以找到它或得到它。

A more performant approach would enumerate through the list; 通过列表可以列举出性能更高的方法; if an element appears out of order, it is unsorted, and otherwise it is sorted. 如果元素出现乱序,则表示未排序,否则进行排序。

Here is one (basic) implementation using an extension method: 这是使用扩展方法的一种(基本)实现:

// define possible orderings
public enum SortOrder
{
    Ascending,
    Descending,
    Constant,
    Unordered,
    Empty
}

public static class Extensions
{
    // simple method to get the ordering of an enumeration
    public static SortOrder GetOrdering<T>(this IEnumerable<T> list, 
        IComparer<T> comparer = null) where T : IComparable<T>
    {
        if (!list.Any())
            return SortOrder.Empty;
        if (comparer == null)
            comparer = Comparer<T>.Default;
        SortOrder ret = SortOrder.Constant;
        T last = list.First();
        // for each element after the first, compare with previous value
        foreach (T v in list.Skip(1))
        {
            var c = comparer.Compare(last, v);
            last = v;
            if (c == 0) continue;

            if ((c < 0 && ret == SortOrder.Ascending)
                || c > 0 && ret == SortOrder.Descending)
                return SortOrder.Unordered;
            else if (ret == SortOrder.Constant)
                ret = c < 0 ? SortOrder.Descending : SortOrder.Ascending;
        }
        return ret;
    }
}

At this point, the extension method can be used like so: 此时,可以像这样使用扩展方法:

List<int> l1 = new List<int> { 1, 2, 3, 3, 4, 5 };
List<string> l2 = new List<string> { "Z", "z", "z" };
List<int> l3 = new List<int> { 8, 2, 3, 4 };
List<int> l4 = new List<int> { 8, 4, 4, 3, 2 };
Console.WriteLine(l1.GetOrdering()); // returns Ascending
Console.WriteLine(l2.GetOrdering())); // return Ascending
Console.WriteLine(l2.GetOrdering(StringComparer.InvariantCultureIgnoreCase))); // returns Constant
Console.WriteLine(l3.GetOrdering())); // return Unordered
Console.WriteLine(l4.GetOrdering())); // return Descending

Note that since the extension method enumerates through the list, rather than sorts it, performance in the worst case is O(N) for an ordered list. 请注意,由于扩展方法是对列表进行枚举而不是对列表进行排序,因此,最坏情况下的性能对于有序列表为O(N)。

You don't really need the new constraint as you can just use typeof(T) instead of creating the class then doing GetType. 您实际上并不需要新的约束,因为您可以只使用typeof(T)而不是创建类然后执行GetType。 This should be faster since the code just compares it loops over the data once. 这应该更快,因为代码只比较它一次遍历数据。

This method will return back all the properties that it could be ordered by, not just the first (I assumed you wanted that changed based on your comment to your question). 此方法将返回它可以排序的所有属性,而不仅仅是第一个(我假设您希望根据对问题的评论对属性进行更改)。

Like yours if the list is empty it just returns back "none". 与您的列表一样,如果列表为空,则返回“无”。 If they are all equal it will return back the default of "asc". 如果它们相等,则将返回默认值“ asc”。 Unlike yours this code doesn't order the property by their name since it will return back all that are sorted. 与您的代码不同,此代码不会按属性名称排序,因为它将返回所有已排序的属性。

Also you should be careful with IQueryable as that could be a database call and if you loop over it many times you could be hitting a database multiple times. 另外,您应该对IQueryable保持谨慎,因为这可能是数据库调用,并且如果您多次循环遍历,则可能会多次访问数据库。 I switched it to IEnumerable but if you need IQueryable you could switch it back. 我将其切换为IEnumerable,但如果需要IQueryable,则可以将其切换回。

private string GetOrder<T>(IEnumerable<T> list)
    where T : class
{
    // Comparer class to see greater or less than or equal
    var comparer = Comparer.Default;

    // Track currrent orderby so far
    var propsOrderBy = typeof (T).GetProperties()
                                 .Where(p => p.CanRead)
                                 .ToDictionary(p => p, _ => 0);

    // List of all the property since we can update dictionary if we iterate over it
    var props = new List<PropertyInfo>(propsOrderBy.Keys);
    // Last value
    var previousValues = new Dictionary<string, object>();

    // Need to iterate over list to compare data
    foreach (var data in list)
    {
        // Check each property if we haven't removed it yet
        foreach (var prop in props.Where(propsOrderBy.ContainsKey))
        {
            var value = prop.GetValue(data);
            if (previousValues.ContainsKey(prop.Name))
            {
                var comparison = comparer.Compare(value, previousValues[prop.Name]);
                // If comparison didn't say it was equal and it's not the same as the previous order by
                if (comparison != 0 && (comparison != propsOrderBy[prop]))
                {
                    // If zero first time setting it
                    if (propsOrderBy[prop] == 0)
                    {
                        propsOrderBy[prop] = comparison;
                    }
                    else
                    {
                        // Not our order key so remove it
                        propsOrderBy.Remove(prop);
                    }
                }
            }
            // Store last value
            previousValues[prop.Name] = value;
        }

        // No point to keep looping if we know all fields have no order
        if (!propsOrderBy.Any())
        {
            break;
        }
    }

    if (previousValues.Any() && propsOrderBy.Any())
    {
        return String.Join(", ", propsOrderBy.Select(p => p.Key.Name + (p.Value < 0 ? " desc" : " asc ")));
    }
    else
    {
        return "none";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM