Natural Sort Order in C#

后端 未结 17 2084
野性不改
野性不改 2020-11-21 04:54

Anyone have a good resource or provide a sample of a natural order sort in C# for an FileInfo array? I am implementing the IComparer interface in

17条回答
  •  野性不改
    2020-11-21 05:46

    We had a need for a natural sort to deal with text with the following pattern:

    "Test 1-1-1 something"
    "Test 1-2-3 something"
    ...
    

    For some reason when I first looked on SO, I didn't find this post and implemented our own. Compared to some of the solutions presented here, while similar in concept, it could have the benefit of maybe being simpler and easier to understand. However, while I did try to look at performance bottlenecks, It is still a much slower implementation than the default OrderBy().

    Here is the extension method I implement:

    public static class EnumerableExtensions
    {
        // set up the regex parser once and for all
        private static readonly Regex Regex = new Regex(@"\d+|\D+", RegexOptions.Compiled | RegexOptions.Singleline);
    
        // stateless comparer can be built once
        private static readonly AggregateComparer Comparer = new AggregateComparer();
    
        public static IEnumerable OrderByNatural(this IEnumerable source, Func selector)
        {
            // first extract string from object using selector
            // then extract digit and non-digit groups
            Func> splitter =
                s => Regex.Matches(selector(s))
                          .Cast()
                          .Select(m => Char.IsDigit(m.Value[0]) ? (IComparable) int.Parse(m.Value) : m.Value);
            return source.OrderBy(splitter, Comparer);
        }
    
        /// 
        /// This comparer will compare two lists of objects against each other
        /// 
        /// Objects in each list are compare to their corresponding elements in the other
        /// list until a difference is found.
        private class AggregateComparer : IComparer>
        {
            public int Compare(IEnumerable x, IEnumerable y)
            {
                return
                    x.Zip(y, (a, b) => new {a, b})              // walk both lists
                     .Select(pair => pair.a.CompareTo(pair.b))  // compare each object
                     .FirstOrDefault(result => result != 0);    // until a difference is found
            }
        }
    }
    

    The idea is to split the original strings into blocks of digits and non-digits ("\d+|\D+"). Since this is a potentially expensive task, it is done only once per entry. We then use a comparer of comparable objects (sorry, I can't find a more proper way to say it). It compares each block to its corresponding block in the other string.

    I would like feedback on how this could be improved and what the major flaws are. Note that maintainability is important to us at this point and we are not currently using this in extremely large data sets.

提交回复
热议问题