[英]LINQ: Get element with highest of two/multiple values
I have a list where each element contains two values (V1 and V2). 我有一个列表,其中每个元素包含两个值(V1和V2)。 What I need is the element with the highest V1 and highest V2 (prioritizing V1).
我需要的是具有最高V1和最高V2(优先级为V1)的元素。
I have tried two approaches: 我尝试了两种方法:
OrderByDescending and ThenByDescending, then take the first element: OrderByDescending和ThenByDescending,然后取第一个元素:
list.OrderByDescending(e => e.V1).ThenByDescending(e => e.V2).First();
Select elements with biggest V1, then select the first element with the biggest V2 from this enumerable: 选择具有最大V1的元素,然后从该枚举中选择具有最大V2的第一个元素:
var maxV1 = l.Where(e => e.V1 == l.Max(e => e.V1)); maxV1.First(e => e.V2 == maxV1.Max(e1 => e1.V2));
Both (in my use case) require a fair amount of time and I'm not satisfied with either of my solutions. 两者(在我的用例中)需要相当长的时间,我对我的任何一种解决方案都不满意。
The list itself doesn't contain a lot of elements, not more than 100. But there are a lot of them. 列表本身不包含很多元素,不超过100个。但是它们有很多。
Is there another, preferably more efficient, solution than what I've already tried? 还有另一个,最好是更有效的解决方案,而不是我已经尝试过的解决方案吗? Or do I have to rethink the whole architecture?
或者我是否需要重新考虑整个架构?
Edit: I forgot to mention that there are more variables in each element which might be used to select the highest value. 编辑:我忘了提到每个元素中有更多变量可用于选择最高值。 Which one is used depends on a parameter.
使用哪一个取决于参数。 So pre sorting using sorted collections doesn't net any benefits.
因此,使用已排序集合进行预排序并不会带来任何好处
You can use GroupBy
, then order this V1-group by V2: 您可以使用
GroupBy
,然后按V2订购此V1组:
var highestItemByV1V2 = list.GroupBy(x => x.V1)
.OrderByDescending(g => g.Key)
.Select(g => g.OrderByDescending(x => x.V2).First())
.First();
You should also store the max value instead of using it as expression in the query, otherwise it will be evaulated always. 您还应该存储最大值,而不是在查询中将其用作表达式,否则将始终对其进行评估。 So this 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 perform well, it's simple and efficient: 但是,您的第一种方法应该表现良好,它简单而有效:
list.OrderByDescending(e => e.V1).ThenByDescending(e => e.V2).First();
So what kind of performance issue do you have? 那么你有什么样的性能问题? Maybe you are loooking at the wrong place or you call this code too often.
也许你在错误的地方闲逛,或者你经常调用这段代码。 Consider to store them already sorted, fe in a
SortedList
. 考虑将它们已经排序,存储在
SortedList
。 I think that a SortedDictionary
is even more efficient in this case. 我认为在这种情况下,
SortedDictionary
更有效 。
The
SortedDictionary<TKey, TValue>
generic class is a binary search tree with O(log n) retrieval, where n is the number of elements in the dictionary.SortedDictionary<TKey, TValue>
泛型类是具有O(log n)检索的二叉搜索树,其中n是字典中元素的数量。 In this respect, it is similar to theSortedList<TKey, TValue>
generic class.在这方面,它类似于
SortedList<TKey, TValue>
泛型类。 The two classes have similar object models, and both have O(log n) retrieval.这两个类具有相似的对象模型,并且都具有O(log n)检索。 Where the two classes differ is in memory use and speed of insertion and removal:
两个类别的不同之处在于内存使用和插入和移除速度:
SortedList<TKey, TValue>
uses less memory than SortedDictionary<TKey, TValue>
. SortedList<TKey, TValue>
使用的内存少于SortedDictionary<TKey, TValue>
。 SortedDictionary<TKey, TValue>
has faster insertion and removal operations for unsorted data: O(log n) as opposed to O(n) for SortedList<TKey, TValue>
. SortedDictionary<TKey, TValue>
对未排序数据具有更快的插入和删除操作:O(log n),而不是SortedList<TKey, TValue>
O(n)。 SortedList<TKey, TValue>
is faster than SortedDictionary<TKey, TValue>
. SortedList<TKey, TValue>
比SortedDictionary<TKey, TValue>
快。 Here is a possible implementation using a SortedDictionary<double, SortedSet<Obj>>
: 这是使用
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 have presumed that V1
is a primitive type like double
. Obj
是你的类包含V1
和V2
,我假设V1
是一个像double
这样的原始类型。 GetMaxItem
is the method that returns the max-item. GetMaxItem
是返回max-item的方法。
If V1
and V2
can contain duplicates you could try this approach, where the key of each SortedDictionary
is the V1
value and the value is another SortedDictionary
with the V2
-key and all related objects. 如果
V1
和 V2
可以包含重复项,则可以尝试这种方法,其中每个SortedDictionary
的键是V1
值,值是带有V2
key和所有相关对象的另一个SortedDictionary
。
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;
}
Non-LINQ (I took System.Drawing.Point struct for this example): 非LINQ(我为此示例使用了System.Drawing.Point结构):
static Point GetHighestXY(Point[] points)
{
Point max = default(Point);
for (int i = 0; i < points.Length; i++)
{
if (points[i].X < max.X) continue;
if (points[i].X > max.X) { max = points[i]; }
else { if (points[i].Y > max.Y) max = points[i]; }
}
return max;
}
Usage example: 用法示例:
Point[] pts =
{
new Point(55, 8),
new Point(55, 10),
new Point(10, 10),
new Point(22, 11),
new Point(16, 33),
new Point(4, 104)
};
Point max = GetHighestXY(pts);
Console.WriteLine("X : {0} Y : {1} ", max.X, max.Y);
Result : 结果:
As always, if you just want the maximum value, there's no need to do any sorting - Aggregate
is O(n): 与往常一样,如果您只想要最大值,则无需进行任何排序 -
Aggregate
为O(n):
var maxByBoth = items.Aggregate(
(bestSoFar, current) =>
{
if (current.V1 > bestSoFar.V1)
return current;
if (current.V1 == bestSoFar.V1 && current.V2 > bestSoFar.V2)
return current;
return bestSoFar;
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.