简体   繁体   English

LINQ:获取具有两个/多个值中最高的元素

[英]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: 我尝试了两种方法:

  1. OrderByDescending and ThenByDescending, then take the first element: OrderByDescending和ThenByDescending,然后取第一个元素:

     list.OrderByDescending(e => e.V1).ThenByDescending(e => e.V2).First(); 
  2. 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 the SortedList<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)。
  • If the list is populated all at once from sorted data, 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是你的类包含V1V2 ,我假设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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM