List.Sort with lambda expression - c#

I'm trying to sort part of a list with a lambda expression, but I get an error when trying to do so:
List<int> list = new List<int>();
list.Add(1);
list.Add(3);
list.Add(2);
list.Add(4);
// works fine
list.Sort((i1, i2) => i1.CompareTo(i2) );
// "Cannot convert lambda expression to type 'System.Collections.Generic.IComparer<int>' because it is not a delegate type"
list.Sort(1, 2, (i1, i2) => i1.CompareTo(i2) );
foreach (int i in list)
Console.WriteLine(i);
At a guess this is because there's no System.Comparison overload for the sort that takes a range. Is this omitted for any particular reason?
Is there an easy way of getting a suitable IComparer from the lambda expression (like a class I can just use to go list.Sort(1, 2, new CompareyThing<int>((...) => ...)) or something)?

You can use the Comparer.Create method, although this appears to be new in .Net 4.5
list.Sort(1, 2, Comparer<int>.Create((i1, i2) => i1.CompareTo(i2)));
You can always create your own comparer:
public class FuncComparer<T> : IComparer<T>
{
private readonly Func<T, T, int> func;
public FuncComparer(Func<T, T, int> comparerFunc)
{
this.func = comparerFunc;
}
public int Compare(T x, T y)
{
return this.func(x, y);
}
}
Then your code would be:
list.Sort(1, 2, new FuncComparer<int>((i1, i2) => i1.CompareTo(i2)));

You could create a custom comparer if you're not using .Net 4.5:
class IntComparer : IComparer<int>
{
public int Compare(int x, int y)
{
return x.CompareTo(y);
}
}
list.Sort(1, 2, new IntComparer());

Related

IComparer not working as expected

My program's function is to count the occurrences of unique words in a document, and then display them in sorted order. I first loop through all the words and enter them into a dictionary, and increment the value in the dictionary for how many times they have been encountered. I then convert the dictionary to a list and call the .Sort method with an IComparer as a parameter. Shown in this code here:
List<KeyValuePair<string,long>> wordList = wordCount.ToList();
IComparer<KeyValuePair<string,long>> comparison = new comparator();
wordList.Sort(comparison);
And the IComparer class I am using
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
if (x.Value > y.Value)
return 1;
else
return 0;
}
}
However, when I am finished with the sorting, the list is not ordered by the value of the KeyValuePair like as I hoped it would be. What am I doing wrong here?
You're missing the case when y.Value is greater than x.Value in your comparer implementation:
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
if (x.Value > y.Value)
{
return 1;
}
else if (x.Value < y.Value)
{
return -1;
}
else
return 0;
}
}
or
public class comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
return x.Value.CompareTo(y.Value);
}
}
You could also use LINQ OrderBy instead of Sort. It's easier to use because it takes a lambda expression, but it will create a new collection, instead of sorting the provided one.
var sorted = wordList.OrderByDescending(x => x.Value).ToList();
You could do all your processing in one query (assuming words is a collection of strings with all the words):
var sortedWithCount = words.GroupBy(x => x)
.OrderByDescending(g => g.Count)
.ToList(g => new { Word = g.Key, Count = g.Count });
Actually, you should return 1, 0 and -1 as a result fo the Compare method. But in your case, you could just use the CompareTo method from long type, for sample:
public class Comparator : IComparer<KeyValuePair<string, long>>
{
public int Compare(KeyValuePair<string, long> x, KeyValuePair<string, long> y)
{
return x.Value.CompareTo(y.Value);
}
}
As a good pratice, rename your class to Comparator and not comparator. Keep the clean code on!

OutOfMemoryException with big collection and OrderBy?

i have a collection trends with about 30kk elements. When i am trying execute following code in linqpad
trends.Take(count).Dump();
it works ok.
But if i add sort:
trends.OrderByDescending(x => x.Item2).Take(count).Dump();
I get System.OutOfMemoryException
What i am doing wrong?
OrderByDescending (or OrderBy) materializes the whole sequence when you try to fetch the first element - it has to, as otherwise you can't possibly know the first element. It has to make a copy of the sequence (typically just a bunch of references, of course) in order to sort, so if the original sequence is an in-memory collection, you end up with two copies of it. Presumably you don't have enough memory for that.
You don't have to sort the whole collection just take top count elements from it. Here is a solution for this https://codereview.stackexchange.com/a/9777/11651.
The key point from this answer is It doesn't require all items to be kept in memory(for sorting)
Again from comments of the answer in the link:
The idea is: You can find the Max(or Min) item of a List in O(n) time. if you extend this idea to m item(5 in the question), you can get top(or buttom) m items faster then sorting the list(just in one pass on the list + the cost of keeping 5 sorted items)
Here is another extension method that may work better than the original LINQ (e.g. it shouldn't blow up for a small number of selected items). Like L.B.'s solution it should be O(n) and doesn't keep all items in memory:
public static class Enumerables
{
public static IEnumerable<T> TopN<T, TV>(this IEnumerable<T> value, Func<T, TV> selector, Int32 count, IComparer<TV> comparer)
{
var qCount = 0;
var queue = new SortedList<TV, List<T>>(count, comparer);
foreach (var val in value)
{
var currTv = selector(val);
if (qCount >= count && comparer.Compare(currTv, queue.Keys[0]) <= 0) continue;
if (qCount == count)
{
var list = queue.Values[0];
if (list.Count == 1)
queue.RemoveAt(0);
else
list.RemoveAt(0);
qCount--;
}
if (queue.ContainsKey(currTv))
queue[currTv].Add(val);
else
queue.Add(currTv, new List<T> {val});
qCount++;
}
return queue.SelectMany(kvp => kvp.Value);
}
public static IEnumerable<T> TopN<T, TV>(this IEnumerable<T> value, Func<T, TV> selector, Int32 count)
{
return value.TopN(selector, count, Comparer<TV>.Default);
}
public static IEnumerable<T> BottomN<T, TV>(this IEnumerable<T> value, Func<T, TV> selector, Int32 count, IComparer<TV> comparer)
{
return value.TopN(selector, count, new ReverseComparer<TV>(comparer));
}
public static IEnumerable<T> BottomN<T, TV>(this IEnumerable<T> value, Func<T, TV> selector, Int32 count)
{
return value.BottomN(selector, count, Comparer<TV>.Default);
}
}
// Helper class
public class ReverseComparer<T> : IComparer<T>
{
private readonly IComparer<T> _comparer;
public int Compare(T x, T y)
{
return -1*_comparer.Compare(x, y);
}
public ReverseComparer()
: this(Comparer<T>.Default)
{ }
public ReverseComparer(IComparer<T> comparer)
{
if (comparer == null) throw new ArgumentNullException("comparer");
_comparer = comparer;
}
}
And some tests:
[TestFixture]
public class EnumerablesTests
{
[Test]
public void TestTopN()
{
var input = new[] { 1, 2, 8, 3, 6 };
var output = input.TopN(n => n, 3).ToList();
Assert.AreEqual(3, output.Count);
Assert.IsTrue(output.Contains(8));
Assert.IsTrue(output.Contains(6));
Assert.IsTrue(output.Contains(3));
}
[Test]
public void TestBottomN()
{
var input = new[] { 1, 2, 8, 3, 6 };
var output = input.BottomN(n => n, 3).ToList();
Assert.AreEqual(3, output.Count);
Assert.IsTrue(output.Contains(1));
Assert.IsTrue(output.Contains(2));
Assert.IsTrue(output.Contains(3));
}
[Test]
public void TestTopNDupes()
{
var input = new[] { 1, 2, 8, 8, 3, 6 };
var output = input.TopN(n => n, 3).ToList();
Assert.AreEqual(3, output.Count);
Assert.IsTrue(output.Contains(8));
Assert.IsTrue(output.Contains(6));
Assert.IsFalse(output.Contains(3));
}
[Test]
public void TestBottomNDupes()
{
var input = new[] { 1, 1, 2, 8, 3, 6 };
var output = input.BottomN(n => n, 3).ToList();
Assert.AreEqual(3, output.Count);
Assert.IsTrue(output.Contains(1));
Assert.IsTrue(output.Contains(2));
Assert.IsFalse(output.Contains(3));
}
}

Using IEqualityComparer for Union

I simply want to remove duplicates from two lists and combine them into one list. I also need to be able to define what a duplicate is. I define a duplicate by the ColumnIndex property, if they are the same, they are duplicates. Here is the approach I took:
I found a nifty example of how to write inline comparers for the random occassions where you need em only once in a code segment.
public class InlineComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> getEquals;
private readonly Func<T, int> getHashCode;
public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
getEquals = equals;
getHashCode = hashCode;
}
public bool Equals(T x, T y)
{
return getEquals(x, y);
}
public int GetHashCode(T obj)
{
return getHashCode(obj);
}
}
Then I just have my two lists, and attempt a union on them with the comparer.
var formatIssues = issues.Where(i => i.IsFormatError == true);
var groupIssues = issues.Where(i => i.IsGroupError == true);
var dupComparer = new InlineComparer<Issue>((i1, i2) => i1.ColumnInfo.ColumnIndex == i2.ColumnInfo.ColumnIndex,
i => i.ColumnInfo.ColumnIndex);
var filteredIssues = groupIssues.Union(formatIssues, dupComparer);
The result set however is null.
Where am I going astray?
I have already confirmed that the two lists have columns with equal ColumnIndex properties.
I've just run your code on a test set.... and it works!
public class InlineComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> getEquals;
private readonly Func<T, int> getHashCode;
public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
getEquals = equals;
getHashCode = hashCode;
}
public bool Equals(T x, T y)
{
return getEquals(x, y);
}
public int GetHashCode(T obj)
{
return getHashCode(obj);
}
}
class TestClass
{
public string S { get; set; }
}
[TestMethod]
public void testThis()
{
var l1 = new List<TestClass>()
{
new TestClass() {S = "one"},
new TestClass() {S = "two"},
};
var l2 = new List<TestClass>()
{
new TestClass() {S = "three"},
new TestClass() {S = "two"},
};
var dupComparer = new InlineComparer<TestClass>((i1, i2) => i1.S == i2.S, i => i.S.GetHashCode());
var unionList = l1.Union(l2, dupComparer);
Assert.AreEqual(3, unionList);
}
So... maybe go back and check your test data - or run it with some other test data?
After all - for a Union to be empty - that suggests that both your input lists are also empty?
A slightly simpler way:
it does preserve the original order
it ignores dupes as it finds them
Uses a link extension method:
formatIssues.Union(groupIssues).DistinctBy(x => x.ColumnIndex)
This is the DistinctBy lambda method from MoreLinq
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
HashSet<TKey> knownKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
Would the Linq Except method not do it for you?
var formatIssues = issues.Where(i => i.IsFormatError == true);
var groupIssues = issues.Where(i => i.IsGroupError == true);
var dupeIssues = issues.Where(i => issues.Except(new List<Issue> {i})
.Any(x => x.ColumnIndex == i.ColumnIndex));
var filteredIssues = formatIssues.Union(groupIssues).Except(dupeIssues);

C#: generic math functions (Min, Max etc.)

I was thinking about writing generic functions for basic Math operations such as Min, Max etc.
But i i dont know how to compare two generic types :
public T Max<T>(T v1, T v2) where T: struct
{
return (v1 > v2 ? v1 : v2);
}
How about that?
Thank you.
You probably want to constrain the generic types to implement IComparable:
public T Max<T>(T v1, T v2) where T: struct, IComparable<T>
and then use the CompareTo method:
{
return (v1.CompareTo(v2) > 0 ? v1 : v2);
}
If you only want to create comparison functions then you could use the default comparer for the type T. For example:
public static T Max<T>(T x, T y)
{
return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
}
If T implements IComparable<T> then that comparer will be used; if T doesn't implement IComparable<T> but does implement IComparable then that comparer will be used; if T doesn't implement either IComparable<T> or IComparable then a runtime exception will be thrown.
If you want/need to do more than just compare the items then you could have a look at the generic operators implementation in MiscUtil and the related article.
Let me disagree.
The #LukeH's implementation is not Generic.
I will explain why it is not Generic:
Comparer<T>.Default involves inspecting T at run-time to determine if it implements IComparable<T>, IComparable or neither.
Although this behavior is not well documented in http://msdn.microsoft.com/en-us/library/azhsac5f.aspx, we can deduct it because Comparer<T>.Default throws an exception when T does not implement neither. If the inspection was done at compilation-time there would be no need for an exception (runtime), with an compile-time error would suffice.
Then, as Comparer<T>.Default uses Reflection, this means a high cost on Run-Time, then...., It is NOT Generic... Why?
Because Generic Programming means: A single algorithm (Generic) can cover many implementations (for many types) maintaining efficiency of hand-written versions.
Take an example. The handwritten version for integers would be:
public static int Max( int x, int y)
{
return (x.CompareTo(y) > 0) ? x : y;
}
It is very simple, involving only a comparison (or maybe more, depending on how Int32.CompareTo() is implemented).
If we use the #LukeH's implementation, we are adding Reflection to something that is very simple.
In short:
Always prefer Compilation-time errors to Run-Time Exceptions ( this is not Javascript, Ruby,... :-) )
This implementation is less efficient compared to the handwritten version (for any type)
On the other hand.
What do you think Max should return when x and y are equivalents?
I'm starting to analyze Real-Generic implementations....
The ideal implementation would be something like...
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
//Pseudo-code ( note the 'or' next to 'where' )
public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
This is not possible in C#, the next try is could be...
//pseudo-code
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
public static T Max<T>(T x, T y) where T: IComparable<T>
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
public static T Max<T>(T x, T y) where T: IComparable
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
But, this is neither possible, because overload resolution doesn't takes into account Generics Constraints....
Then, I'll leave out IComparable consciously. I'm just going to worry about IComparable<T>
public static T Max<T>(T x, T y, Func<T, T, int> cmp)
{
return (cmp(x, y) > 0) ? x : y;
}
public static T Max<T>(T x, T y) where T: IComparable<T>
{
return Max(x, y, (a, b) => { return a.CompareTo(b); });
}
This is a little too late, but why not use dynamic types and delegates as an alternative to IComparable? This way you get compile-type safety in most cases and will only get a runtime error when the types supplied both do not support the operator < and you fail to provide the default comparer as an argument.
public static T Max<T>(T first, T second, Func<T,T,bool> f = null)
{
Func<dynamic,dynamic,bool> is_left_smaller = (x, y) => x < y ? true : false;
var compare = f ?? new Func<T, T, bool>((x, y) => is_left_smaller(x, y));
return compare(first, second) ? second : first;
}
The solution I present on this answer would have worked (I actually did something like this) at the time the question was asked. I'm surprised no answer presents this alternative, thus I'll present it.
You can (and could have used back then) Linq.Expressions (Which was added in .NET 3.5, in 2007, making it a valid answer at the time of the question).
For starters:
using System.Linq.Expressions;
// ...
public T Max<T>(T v1, T v2)
{
var expression = Expression.GreaterThan
(
Expression.Constant(v1),
Expression.Constant(v2)
);
return Expression.Lambda<Func<bool>>(expression).Compile()() ? v1 : v2);
}
That has no need of dynamic, or Comparison<T>/IComparer<T>.
I believe that having a way to specify a custom comparison is better, but that is not what we are doing here. And of course, any type for which the presented solution works, Comparer<T>.Default would work. However, using Linq.Expressions would allow you implement any arithmetic operations. Take this as illustration of that approach.
There is, of course, overhead. Let us have a version that does compile to a function with parameters, we can think later about how to cache it:
using System.Linq.Expressions;
// ...
public T Max<T>(T v1, T v2)
{
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var lambda = Expression.Lambda<Func<T, T, bool>>
(
Expression.GreaterThan(a, b),
new[]{a, b}
);
return ((Func<T, T, bool>)lambda.Compile())(v1, v2) ? v1 : v2;
}
Alright, to cache it, let us start with the generic class approach, which is easier to write:
using System.Linq.Expressions;
class GenericMath<T>
{
private static Func<T, T, bool>? _greaterThan;
public static Func<T, T, bool> GetGreaterThan()
{
if (_greaterThan == null)
{
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var lambda = Expression.Lambda<Func<T, T, bool>>
(
Expression.GreaterThan(a, b),
new[]{a, b}
);
_greaterThan = (Func<T, T, bool>)lambda.Compile();
}
return _greaterThan;
}
public static T Max(T v1, T v2)
{
return GetGreaterThan()(v1, v2) ? v1 : v2;
}
}
Of course, statics on generics has drawbacks (there is a performance cost, plus the memory is never released). We can start our way to a better solution by using a dictionary cache instead:
using System.Linq.Expressions;
class GenericMath
{
private readonly static Dictionary<Type, Delegate> _gtCache = new Dictionary<Type, Delegate>();
public static Func<T, T, bool> GetGreaterThan<T>()
{
if (!_gtCache.TryGetValue(typeof(T), out var #delegate) || #delegate == null)
{
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var lambda = Expression.Lambda<Func<T, T, bool>>
(
Expression.GreaterThan(a, b),
new[]{a, b}
);
#delegate = lambda.Compile();
_addCache[typeof(T)] = #delegate;
}
return (Func<T, T, bool>)#delegate;
}
public static T Max<T>(T v1, T v2)
{
return GetGreaterThan<T>()(v1, v2) ? v1 : v2;
}
}
Ok, I hear you, I have introduced a new problem: that solution is not thread-safe.
We could use ConcurrentDictionary (added in .NET 4.0, which -if I'm not mistaken- was on beta at the time of the question), but we would not be releasing memory anyway. Instead, we can make a custom class for this use:
public sealed class TypeCacheDict<TValue>
{
private const int Capacity = 256;
private readonly Entry[] _entries;
public TypeCacheDict()
{
_entries = new Entry[Capacity];
}
public TValue this[Type key]
{
get
{
if (TryGetValue(key, out var value))
{
return value;
}
throw new KeyNotFoundException();
}
set => Add(key, value);
}
public void Add(Type key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var hash = key.GetHashCode();
var index = hash & (_entries.Length - 1);
var entry = _entries[index];
Thread.MemoryBarrier();
if (entry?.Hash != hash || !entry.Key.Equals(key))
{
Interlocked.Exchange(ref _entries[index], new Entry(hash, key, value));
}
}
public bool TryGetValue(Type key, out TValue value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var hash = key.GetHashCode();
var index = hash & (_entries.Length - 1);
var entry = _entries[index];
Thread.MemoryBarrier();
if (entry?.Hash == hash && entry.Key.Equals(key))
{
value = entry.Value;
return value != null;
}
value = default;
return false;
}
private sealed class Entry
{
internal readonly int Hash;
internal readonly Type Key;
internal readonly TValue Value;
internal Entry(int hash, Type key, TValue value)
{
Hash = hash;
Key = key;
Value = value;
}
}
}
This TypeCacheDict is thread-safe. First of all Entry is immutable. We don't need to worry about shared access to it. Plus, it a reference type, so replacing it is an atomic operation. We are using Thread.MemoryBarrier and Interlocked.Exchange o mimic Volatile.Read and Volatile.Write because Volatile was not available (and Thread.Volatile* lacks generic overload, and I would rather not instroduce extra casts).
With this new type we can now write:
private readonly static TypeCacheDict<Delegate> _gtCache = new TypeCacheDict<Delegate>();
The rest of the code could be left unchanged. Although there is room for improvement: TryGetOrAdd:
public TValue TryGetOrAdd(Type key, Func<TValue> valueFactory)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (valueFactory == null)
{
throw new ArgumentNullException(nameof(valueFactory));
}
var hash = key.GetHashCode();
var index = hash & (_entries.Length - 1);
var entry = _entries[index];
Thread.MemoryBarrier();
if (entry?.Hash == hash && entry.Key.Equals(key))
{
return entry.Value;
}
var value = valueFactory();
Interlocked.Exchange(ref _entries[index], new Entry(hash, key, value));
return value;
}
Which allows us to write:
public static Func<T, T, bool> GetGreaterThan<T>()
{
return (Func<T, T, bool>)_gtCache.TryGetOrAdd
(
typeof(T),
()=>
{
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var lambda = Expression.Lambda<Func<T, T, bool>>(Expression.GreaterThan(a, b), new[]{a, b});
return lambda.Compile();
}
);
}
Of course, this is how you use it:
Console.WriteLine(GenericMath.Max<int>(90, 100)); // 100
To demostrate, the power of this approach, this is Add:
private readonly static TypeCacheDict<Delegate> _addCache = new TypeCacheDict<Delegate>();
public static Func<T, T, T> GetAdd<T>()
{
return (Func<T, T, T>)_addCache.TryGetOrAdd
(
typeof(T),
()=>
{
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var lambda = Expression.Lambda<Func<T, T, T>>(Expression.Add(a,b), new[]{a, b});
return lambda.Compile();
}
);
}
public static T Add<T>(T v1, T v2)
{
return GetAdd<T>()(v1, v2);
}
And this is how you use it:
Console.WriteLine(GenericMath.Add<int>(90, 100)); // 190
.NET 7 introduces a new feature - generic math (read more here and here) which is based on addition of static abstract interface methods. This feature introduces a lot of interfaces which allow to generically abstract over number types and/or math operations, which allows to rewrite the function in question as:
public T Max<T>(T v1, T v2) where T: IComparisonOperators<T, T, bool> => v1 > v2 ? v1 : v2;
Note that for build-in number types usually you don't need to define your own Max because it is already defined on the INumber<T> interface:
public interface INumber<TSelf> :
// ... other interfaces,
System.Numerics.IComparisonOperators<TSelf,TSelf,bool>
// ... other interfaces
where TSelf : INumber<TSelf>
From memory, T also needs to be IComparable (add that to the where), and then you use v1.CompareTo(v2) > 0 etc.
Necromancing.
You can have a Max/Min function on an arbitrary number of parameters:
public static T Min<T>(params T[] source)
where T: struct, IComparable<T>
{
if (source == null)
throw new System.ArgumentNullException("source");
T value = default(T);
bool hasValue = false;
foreach (T x in source)
{
if (hasValue)
{
// if (x < value) // https://learn.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
// Less than zero This object precedes the object specified by the CompareTo method in the sort order.
// Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
// Greater than zero
if (x.CompareTo(value) < 0)
value = x;
}
else
{
value = x;
hasValue = true;
}
}
if (hasValue)
return value;
throw new System.InvalidOperationException("Sequence contains no elements");
}
public static T Max<T>(params T[] source)
where T : struct, IComparable<T>
{
if (source == null)
throw new System.ArgumentNullException("source");
T value = default(T);
bool hasValue = false;
foreach (T x in source)
{
if (hasValue)
{
// if (x > value) // https://learn.microsoft.com/en-us/dotnet/api/system.icomparable-1?view=netcore-3.1
// Less than zero This object precedes the object specified by the CompareTo method in the sort order.
// Zero This current instance occurs in the same position in the sort order as the object specified by the CompareTo method argument.
// Greater than zero
if (x.CompareTo(value) > 0)
value = x;
}
else
{
value = x;
hasValue = true;
}
}
if (hasValue)
return value;
throw new System.InvalidOperationException("Sequence contains no elements");
}

Sorting an IList in C#

So I came across an interesting problem today. We have a WCF web service that returns an IList. Not really a big deal until I wanted to sort it.
Turns out the IList interface doesn't have a sort method built in.
I ended up using the ArrayList.Adapter(list).Sort(new MyComparer()) method to solve the problem but it just seemed a bit "ghetto" to me.
I toyed with writing an extension method, also with inheriting from IList and implementing my own Sort() method as well as casting to a List but none of these seemed overly elegant.
So my question is, does anyone have an elegant solution to sorting an IList
You can use LINQ:
using System.Linq;
IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
This question inspired me to write a blog post: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/
I think that, ideally, the .NET Framework would include a static sorting method that accepts an IList<T>, but the next best thing is to create your own extension method. It's not too hard to create a couple of methods that will allow you to sort an IList<T> as you would a List<T>. As a bonus you can overload the LINQ OrderBy extension method using the same technique, so that whether you're using List.Sort, IList.Sort, or IEnumerable.OrderBy, you can use the exact same syntax.
public static class SortExtensions
{
// Sorts an IList<T> in place.
public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
{
ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison));
}
// Sorts in IList<T> in place, when T is IComparable<T>
public static void Sort<T>(this IList<T> list) where T: IComparable<T>
{
Comparison<T> comparison = (l, r) => l.CompareTo(r);
Sort(list, comparison);
}
// Convenience method on IEnumerable<T> to allow passing of a
// Comparison<T> delegate to the OrderBy method.
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
{
return list.OrderBy(t => t, new ComparisonComparer<T>(comparison));
}
}
// Wraps a generic Comparison<T> delegate in an IComparer to make it easy
// to use a lambda expression for methods that take an IComparer or IComparer<T>
public class ComparisonComparer<T> : IComparer<T>, IComparer
{
private readonly Comparison<T> _comparison;
public ComparisonComparer(Comparison<T> comparison)
{
_comparison = comparison;
}
public int Compare(T x, T y)
{
return _comparison(x, y);
}
public int Compare(object o1, object o2)
{
return _comparison((T)o1, (T)o2);
}
}
With these extensions, sort your IList just like you would a List:
IList<string> iList = new []
{
"Carlton", "Alison", "Bob", "Eric", "David"
};
// Use the custom extensions:
// Sort in-place, by string length
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));
// Or use OrderBy()
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length));
There's more info in the post: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/
How about using LINQ To Objects to sort for you?
Say you have a IList<Car>, and the car had an Engine property, I believe you could sort as follows:
from c in list
orderby c.Engine
select c;
Edit: You do need to be quick to get answers in here. As I presented a slightly different syntax to the other answers, I will leave my answer - however, the other answers presented are equally valid.
You're going to have to do something like that i think (convert it into a more concrete type).
Maybe take it into a List of T rather than ArrayList, so that you get type safety and more options for how you implement the comparer.
The accepted answer by #DavidMills is quite good, but I think it can be improved upon. For one, there is no need to define the ComparisonComparer<T> class when the framework already includes a static method Comparer<T>.Create(Comparison<T>). This method can be used to create an IComparison on the fly.
Also, it casts IList<T> to IList which has the potential to be dangerous. In most cases that I have seen, List<T> which implements IList is used behind the scenes to implement IList<T>, but this is not guaranteed and can lead to brittle code.
Lastly, the overloaded List<T>.Sort() method has 4 signatures and only 2 of them are implemented.
List<T>.Sort()
List<T>.Sort(Comparison<T>)
List<T>.Sort(IComparer<T>)
List<T>.Sort(Int32, Int32, IComparer<T>)
The below class implements all 4 List<T>.Sort() signatures for the IList<T> interface:
using System;
using System.Collections.Generic;
public static class IListExtensions
{
public static void Sort<T>(this IList<T> list)
{
if (list is List<T> listImpl)
{
listImpl.Sort();
}
else
{
var copy = new List<T>(list);
copy.Sort();
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
{
if (list is List<T> listImpl)
{
listImpl.Sort(comparison);
}
else
{
var copy = new List<T>(list);
copy.Sort(comparison);
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
{
if (list is List<T> listImpl)
{
listImpl.Sort(comparer);
}
else
{
var copy = new List<T>(list);
copy.Sort(comparer);
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, int index, int count,
IComparer<T> comparer)
{
if (list is List<T> listImpl)
{
listImpl.Sort(index, count, comparer);
}
else
{
var range = new List<T>(count);
for (int i = 0; i < count; i++)
{
range.Add(list[index + i]);
}
range.Sort(comparer);
Copy(range, 0, list, index, count);
}
}
private static void Copy<T>(IList<T> sourceList, int sourceIndex,
IList<T> destinationList, int destinationIndex, int count)
{
for (int i = 0; i < count; i++)
{
destinationList[destinationIndex + i] = sourceList[sourceIndex + i];
}
}
}
Usage:
class Foo
{
public int Bar;
public Foo(int bar) { this.Bar = bar; }
}
void TestSort()
{
IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 };
IList<Foo> foos = new List<Foo>()
{
new Foo(1),
new Foo(4),
new Foo(5),
new Foo(3),
new Foo(2),
};
ints.Sort();
foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar));
}
The idea here is to leverage the functionality of the underlying List<T> to handle sorting whenever possible. Again, most IList<T> implementations that I have seen use this. In the case when the underlying collection is a different type, fallback to creating a new instance of List<T> with elements from the input list, use it to do the sorting, then copy the results back to the input list. This will work even if the input list does not implement the IList interface.
try this **USE ORDER BY** :
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
}
private static IList<Employee> GetItems()
{
List<Employee> lst = new List<Employee>();
lst.Add(new Employee { Id = "1", Name = "Emp1" });
lst.Add(new Employee { Id = "2", Name = "Emp2" });
lst.Add(new Employee { Id = "7", Name = "Emp7" });
lst.Add(new Employee { Id = "4", Name = "Emp4" });
lst.Add(new Employee { Id = "5", Name = "Emp5" });
lst.Add(new Employee { Id = "6", Name = "Emp6" });
lst.Add(new Employee { Id = "3", Name = "Emp3" });
return lst;
}
**var lst = GetItems().AsEnumerable();
var orderedLst = lst.OrderBy(t => t.Id).ToList();
orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));**
Found this thread while I was looking for a solution to the exact problem described in the original post. None of the answers met my situation entirely, however. Brody's answer was pretty close. Here is my situation and solution I found to it.
I have two ILists of the same type returned by NHibernate and have emerged the two IList into one, hence the need for sorting.
Like Brody said I implemented an ICompare on the object (ReportFormat) which is the type of my IList:
public class FormatCcdeSorter:IComparer<ReportFormat>
{
public int Compare(ReportFormat x, ReportFormat y)
{
return x.FormatCode.CompareTo(y.FormatCode);
}
}
I then convert the merged IList to an array of the same type:
ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList
Then sort the array:
Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer
Since one-dimensional array implements the interface System.Collections.Generic.IList<T>, the array can be used just like the original IList.
Useful for grid sorting this method sorts list based on property names. As follow the example.
List<MeuTeste> temp = new List<MeuTeste>();
temp.Add(new MeuTeste(2, "ramster", DateTime.Now));
temp.Add(new MeuTeste(1, "ball", DateTime.Now));
temp.Add(new MeuTeste(8, "gimm", DateTime.Now));
temp.Add(new MeuTeste(3, "dies", DateTime.Now));
temp.Add(new MeuTeste(9, "random", DateTime.Now));
temp.Add(new MeuTeste(5, "call", DateTime.Now));
temp.Add(new MeuTeste(6, "simple", DateTime.Now));
temp.Add(new MeuTeste(7, "silver", DateTime.Now));
temp.Add(new MeuTeste(4, "inn", DateTime.Now));
SortList(ref temp, SortDirection.Ascending, "MyProperty");
private void SortList<T>(
ref List<T> lista
, SortDirection sort
, string propertyToOrder)
{
if (!string.IsNullOrEmpty(propertyToOrder)
&& lista != null
&& lista.Count > 0)
{
Type t = lista[0].GetType();
if (sort == SortDirection.Ascending)
{
lista = lista.OrderBy(
a => t.InvokeMember(
propertyToOrder
, System.Reflection.BindingFlags.GetProperty
, null
, a
, null
)
).ToList();
}
else
{
lista = lista.OrderByDescending(
a => t.InvokeMember(
propertyToOrder
, System.Reflection.BindingFlags.GetProperty
, null
, a
, null
)
).ToList();
}
}
}
Here's an example using the stronger typing. Not sure if it's necessarily the best way though.
static void Main(string[] args)
{
IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 };
List<int> stronglyTypedList = new List<int>(Cast<int>(list));
stronglyTypedList.Sort();
}
private static IEnumerable<T> Cast<T>(IEnumerable list)
{
foreach (T item in list)
{
yield return item;
}
}
The Cast function is just a reimplementation of the extension method that comes with 3.5 written as a normal static method. It is quite ugly and verbose unfortunately.
In VS2008, when I click on the service reference and select "Configure Service Reference", there is an option to choose how the client de-serializes lists returned from the service.
Notably, I can choose between System.Array, System.Collections.ArrayList and System.Collections.Generic.List
using System.Linq;
var yourList = SomeDAO.GetRandomThings();
yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );
That's pretty !ghetto.
Found a good post on this and thought I'd share. Check it out HERE
Basically.
You can create the following class and IComparer Classes
public class Widget {
public string Name = string.Empty;
public int Size = 0;
public Widget(string name, int size) {
this.Name = name;
this.Size = size;
}
}
public class WidgetNameSorter : IComparer<Widget> {
public int Compare(Widget x, Widget y) {
return x.Name.CompareTo(y.Name);
}
}
public class WidgetSizeSorter : IComparer<Widget> {
public int Compare(Widget x, Widget y) {
return x.Size.CompareTo(y.Size);
}
}
Then If you have an IList, you can sort it like this.
List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));
widgets.Sort(new WidgetNameSorter());
widgets.Sort(new WidgetSizeSorter());
But Checkout this site for more information... Check it out HERE
Is this a valid solution?
IList<string> ilist = new List<string>();
ilist.Add("B");
ilist.Add("A");
ilist.Add("C");
Console.WriteLine("IList");
foreach (string val in ilist)
Console.WriteLine(val);
Console.WriteLine();
List<string> list = (List<string>)ilist;
list.Sort();
Console.WriteLine("List");
foreach (string val in list)
Console.WriteLine(val);
Console.WriteLine();
list = null;
Console.WriteLine("IList again");
foreach (string val in ilist)
Console.WriteLine(val);
Console.WriteLine();
The result was:
IList
B
A
C
List
A
B
C
IList again
A
B
C
This looks MUCH MORE SIMPLE if you ask me. This works PERFECTLY for me.
You could use Cast() to change it to IList then use OrderBy():
var ordered = theIList.Cast<T>().OrderBy(e => e);
WHERE T is the type eg. Model.Employee or Plugin.ContactService.Shared.Contact
Then you can use a for loop and its DONE.
ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();
foreach (var item in ordered)
{
ContactItems.Add(item);
}
Convert your IList into List<T> or some other generic collection and then you can easily query/sort it using System.Linq namespace (it will supply bunch of extension methods)

Categories

Resources