Reputation: 1129
I have following class.
public class Header
{
public long CombinedIndex { get; private set; }
public int Key { get; set; } //Unique
public bool IsRequired { get; set; }// True or false
public DateTime? AvailableDate { get; set; } // null or date value
public int Index { get; set; } // a number and can be same among other Header
public void CalculateCombinedIndex()
{
CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
}
}
I am expected around 50,000 instances of it in an array. I need to sort them in following order:
Does it make sense to say sum all these property value and derive a unique number and then only sort that number. Later, the list of Header can be converted to dictionary where Key is CombinedIndex and my already sorted array can lookup that dictionary in order I iterated the array. How efficient and promising this all looks?
Upvotes: 0
Views: 138
Reputation: 54877
If you need to perform comparison operations based on a projected key from a given type, I find it helps to have a KeyComparer
class that can create an IComparer<TSource>
instance from a Func<TSource, TKey>
delegate. This works especially well with the new tuple syntax introduced in C# 7. Here's an example of the resulting syntax:
// Create an IComparer<Header> instance based on your combined key.
var comparer = KeyComparer.Create((Header h) => (h.IsRequired, h.Index, h.AvailableDate, h.Key));
List<Header> headers = ...
// Sort the list using the combined key.
headers.Sort(comparer);
// Convert to a dictionary keyed by the combined key.
var dict = headers.ToDictionary(comparer.KeySelector);
And here's a sample implementation of the class. (For a more detailed explanation, see my blog post.)
public static class KeyComparer
{
public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
Func<TSource, TKey> keySelector,
IComparer<TKey> innerComparer = null)
{
return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
}
}
public class KeyComparer<TSource, TKey> : Comparer<TSource>
{
protected internal KeyComparer(
Func<TSource, TKey> keySelector,
IComparer<TKey> innerComparer = null)
{
KeySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector));
InnerComparer = innerComparer ?? Comparer<TKey>.Default;
}
public Func<TSource, TKey> KeySelector { get; }
public IComparer<TKey> InnerComparer { get; }
public override int Compare(TSource x, TSource y)
{
if (object.ReferenceEquals(x, y))
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
TKey xKey = KeySelector(x);
TKey yKey = KeySelector(y);
return InnerComparer.Compare(xKey, yKey);
}
}
Upvotes: 1
Reputation: 45096
Just implement IComparable and you can use List.Sort().
public class Header : IComparable
{
public long CombinedIndex { get; private set; }
public int Key { get; set; } //Unique
public bool IsRequired { get; set; }// True or false
public DateTime? AvailableDate { get; set; } // null or date value
public int Index { get; set; } // a number and can be same among other Header
public void CalculateCombinedIndex()
{
CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
Header otherHeader = obj as Header;
if (otherHeader != null)
{
if (this.IsRequired && !otherHeader.IsRequired)
return 1;
if (!this.IsRequired && otherHeader.IsRequired)
return -1;
if (this.Index > otherHeader.Index)
return 1;
if (this.Index < otherHeader.Index)
return -1;
//// ....
return 0;
}
else
throw new ArgumentException("Object is not a Temperature");
}
}
Upvotes: 0
Reputation: 460098
I would do it with LINQ:
var ordered = list
.OrderByDescending(x => x.IsRequired)
.ThenBy(x => x.Index)
.ThenBy(x=> x.AvailableDate)
.ThenBy(x=> x.Key);
Only in case of ties the next condition will be used. So no need to create a combined index which makes it very difficult to change the order logic and which seems to be broken anyway.
Upvotes: 2