You can use GroupBy and then order this V1 group by V2:
var highestItemByV1V2 = list.GroupBy(x => x.V1) .OrderByDescending(g => g.Key) .Select(g => g.OrderByDescending(x => x.V2).First()) .First();
You should also keep the maximum value instead of using it as an expression in a query, otherwise it will always be evacuated. Thus, it is more efficient:
var highestV1 = list.Max(x => x.V1); var maxObj = list.Where(x => x.V1 == highestV1).OrderByDescending(x => x.V2).First();
However, your first approach should work well, it is simple and effective:
list.OrderByDescending(e => e.V1).ThenByDescending(e => e.V2).First();
So what is your performance issue? Maybe you are looking at the wrong place or calling this code too often. Consider keeping them already sorted, i.e. in a SortedList . I think SortedDictionary even more efficient in this case.
The general class SortedDictionary<TKey, TValue> is a binary search tree with the extraction of O (log n), where n is the number of elements in the Dictionary. In this respect, it is similar to the general class SortedList<TKey, TValue> . The two classes have similar object models, and both have O (log n) extraction. Where the two classes differ from each other, memory usage and insert and delete speeds are:
SortedList<TKey, TValue> uses less memory than SortedDictionary<TKey, TValue> .SortedDictionary<TKey, TValue> has a faster insert and delete operation for unsorted data: O (log n) as opposed to O (n) for SortedList<TKey, TValue> .- If the list is populated immediately from the sorted data,
SortedList<TKey, TValue> faster than SortedDictionary<TKey, TValue> .
Here is a possible implementation using SortedDictionary<double, SortedSet<Obj>> :
SortedDictionary<double, SortedSet<Obj>> sortedLookup = new SortedDictionary<double, SortedSet<Obj>>(); // key is V1 and value all items with that value internal class ObjV2Comparer : IComparer<Obj> { public int Compare(Obj x, Obj y) { return x.V2.CompareTo(y.V2); } } private static readonly ObjV2Comparer V2Comparer = new ObjV2Comparer(); public void Add(Obj obj) { SortedSet<Obj> set; bool exists = sortedLookup.TryGetValue(obj.V1, out set); if(!exists) set = new SortedSet<Obj>(V2Comparer); set.Add(obj); sortedLookup[obj.V1] = set; } public Obj GetMaxItem() { if (sortedLookup.Count == 0) return null; Obj maxV1Item = sortedLookup.Last().Value.Last(); return maxV1Item; }
Obj is your class that contains V1 and V2 , I assumed that V1 is a primitive type of type double . GetMaxItem is a method that returns max-item.
If V1 and V2 can contain duplicates, you can try this approach, where the key of each SortedDictionary is the value of V1 , and the value is another SortedDictionary with the key of V2 and all related objects.
SortedDictionary<double, SortedDictionary<double, List<Obj>>> sortedLookup = new SortedDictionary<double, SortedDictionary<double, List<Obj>>>(); public void Add(Obj obj) { SortedDictionary<double, List<Obj>> value; bool exists = sortedLookup.TryGetValue(obj.V1, out value); if(!exists) { value = new SortedDictionary<double, List<Obj>>(){{obj.V2, new List<Obj>{obj}}}; sortedLookup.Add(obj.V1, value); } else { List<Obj> list; exists = value.TryGetValue(obj.V2, out list); if (!exists) list = new List<Obj>(); list.Add(obj); value[obj.V2] = list; sortedLookup[obj.V1] = value; } } public Obj GetMaxItem() { if (sortedLookup.Count == 0) return null; Obj maxV1Item = sortedLookup.Last().Value.Last().Value.Last(); return maxV1Item; }