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
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)
{
...
}
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.