Custom Lambda sort extension

后端 未结 2 1448
眼角桃花
眼角桃花 2021-01-17 06:25

i want to use my custom extension method to order a list of objects. it\'s just a sample so it uses a bubblesort. my current state:

public static IOrderedQue         


        
2条回答
  •  醉酒成梦
    2021-01-17 07:00

    Now... Doing correctly a OrderBy as other said is difficult... this is an implementation I wrote in 15 minutes...

    public static class MyOrderedEnumerable
    {
        public static IOrderedEnumerable MyOrderBy(this IEnumerable source, Func keySelector, IComparer comparer = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException();
            }
    
            if (keySelector == null)
            {
                throw new ArgumentNullException();
            }
    
            if (comparer == null)
            {
                comparer = Comparer.Default;
            }
    
            Comparison comparer2 = (x, y) => comparer.Compare(keySelector(x), keySelector(y));
            return new OrderedEnumerableImpl(source, comparer2);
        }
    
        public static IOrderedEnumerable MyOrderByDescending(this IEnumerable source, Func keySelector, IComparer comparer = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException();
            }
    
            if (keySelector == null)
            {
                throw new ArgumentNullException();
            }
    
            if (comparer == null)
            {
                comparer = Comparer.Default;
            }
    
            Comparison comparer2 = (x, y) => comparer.Compare(keySelector(y), keySelector(x));
            return new OrderedEnumerableImpl(source, comparer2);
        }
    
        private class OrderedEnumerableImpl : IOrderedEnumerable
        {
            private readonly IEnumerable Source;
            private readonly Comparison Comparer;
    
            public OrderedEnumerableImpl(IEnumerable source, Comparison comparer)
            {
                Source = source;
                Comparer = comparer;
            }
    
            public IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer comparer, bool descending)
            {
                if (comparer == null)
                {
                    comparer = Comparer.Default;
                }
    
                Comparison comparer2;
    
                if (descending)
                {
                    comparer2 = (x, y) =>
                    {
                        int result = Comparer(x, y);
                        if (result == 0)
                        {
                            result = comparer.Compare(keySelector(y), keySelector(x));
                        }
                        return result;
                    };
                }
                else
                {
                    comparer2 = (x, y) =>
                    {
                        int result = Comparer(x, y);
                        if (result == 0)
                        {
                            result = comparer.Compare(keySelector(x), keySelector(y));
                        }
                        return result;
                    };
                }
    
                return new OrderedEnumerableImpl(Source, comparer2);
            }
    
            public IEnumerator GetEnumerator()
            {
                var source = Source.ToArray();
    
                // ** Here you do the sorting! **
                Array.Sort(source, Comparer);
    
                for (int i = 0; i < source.Length; i++)
                {
                    yield return source[i];
                }
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }
    

    See the ** Here you do the sorting! **? There you have to plug your sort algorithm. As you can see, there is an OrderedEnumerableImpl class that does the implementation. The point is that the ThenBy uses the CreateOrderedEnumerable method. This method modifies the sorting algorithm, by chaining its comparer to the base comparer of the OrderBy. but there is a point here: you can't really modify the ordering, because one could:

    var myorderedcoll = mycoll.OrderBy(x => x.Foo);
    var mysubordered1 = myorderedcoll.ThenBy(x => x.Bar1);
    var mysubordered2 = myorderedcoll.ThenBy(x => x.Bar2);
    

    Clearly mysubordered1 is ordered by Foo+Bar1, while mysubordered2 is ordered by Foo+Bar2!

    So CreateOrderedEnumerable creates a new OrderedEnumerableImpl class, by taking the comparer already present and adding the new comparer (see the last line of CreateOrderedEnumerable)

    Note that you use this class as:

    var myorderedcoll = mycoll.MyOrderBy(x => x.Foo).ThenBy(x => x.Bar);
    

    and so on.

    Note that a "real" implementation is much more complex. As written, this implementation recalculates the key each time it does a comparison. Real implementations precalculate all the necessary keys to speedup the comparisons.

    For the sorting, using the wiki:

    public IEnumerator GetEnumerator()
    {
        var source = Source.ToArray();
    
        // Taken from http://en.wikipedia.org/wiki/Bubble_sort#Pseudocode_implementation
    
        int n = source.Length;
    
        do
        {
            int newn = 0;
    
            for (int i = 1; i <= n - 1; i++)
            {
                if (Comparer(source[i - 1], source[i]) > 0)
                {
                    TSource temp = source[i - 1];
                    source[i - 1] = source[i];
                    source[i] = temp;
                    newn = i;
                }
            }
    
            n = newn;
        }
        while (n != 0);
    
        for (int i = 0; i < source.Length; i++)
        {
            yield return source[i];
        }
    }
    

    This too could be optimized: if we reverse the bubblesort and accumulate the ordered elements in the "top" of the array, then we can for each cycle of bubblesort yield return one element. This will shorten the time needed to have the first element. So if you do OrderBy(x => x.Foo).First(), not all the collection will be sorted.

提交回复
热议问题