Custom Lambda sort extension

后端 未结 2 1370
眼角桃花
眼角桃花 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 06:34

    You are calling comparer.Compare(Result[j - 1], Result[j]) now. That is wrong, since it doesn't take the key into account as you said.

    You should get the key by calling the Func keySelector, and match that one:

    IComparer<TKey> comparer = Comparer<TKey>.Default;
    
    var x = keySelector(Result[j - 1]);
    var y = keySelector(Result[j]);
    
    if (comparer.Compare(x, y) > 0)
    {
        ...
    }
    
    0 讨论(0)
  • 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<TSource> MyOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException();
            }
    
            if (keySelector == null)
            {
                throw new ArgumentNullException();
            }
    
            if (comparer == null)
            {
                comparer = Comparer<TKey>.Default;
            }
    
            Comparison<TSource> comparer2 = (x, y) => comparer.Compare(keySelector(x), keySelector(y));
            return new OrderedEnumerableImpl<TSource>(source, comparer2);
        }
    
        public static IOrderedEnumerable<TSource> MyOrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException();
            }
    
            if (keySelector == null)
            {
                throw new ArgumentNullException();
            }
    
            if (comparer == null)
            {
                comparer = Comparer<TKey>.Default;
            }
    
            Comparison<TSource> comparer2 = (x, y) => comparer.Compare(keySelector(y), keySelector(x));
            return new OrderedEnumerableImpl<TSource>(source, comparer2);
        }
    
        private class OrderedEnumerableImpl<TSource> : IOrderedEnumerable<TSource>
        {
            private readonly IEnumerable<TSource> Source;
            private readonly Comparison<TSource> Comparer;
    
            public OrderedEnumerableImpl(IEnumerable<TSource> source, Comparison<TSource> comparer)
            {
                Source = source;
                Comparer = comparer;
            }
    
            public IOrderedEnumerable<TSource> CreateOrderedEnumerable<TKey>(Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool descending)
            {
                if (comparer == null)
                {
                    comparer = Comparer<TKey>.Default;
                }
    
                Comparison<TSource> 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<TSource>(Source, comparer2);
            }
    
            public IEnumerator<TSource> 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<TSource> 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.

    0 讨论(0)
提交回复
热议问题