324

2つのintプロパティを持つオブジェクトのリストがあります。このリストは、別のlinqクエリの出力です。オブジェクト:

public class DimensionPair  
{
    public int Height { get; set; }
    public int Width { get; set; }
}

Heightリスト内で最大のプロパティ値を持つオブジェクトを見つけて返したいのですが。

値の最大値を取得することはできHeightますが、オブジェクト自体は取得できません。

Linqでこれを行うことはできますか?どのように?

4

9 に答える 9

316

MoreLINQでこれを正確に行うための拡張メソッドがあります。そこでの実装を見ることができますが、基本的には、これまでに見た最大の要素と、投影の下で生成された最大の値を記憶して、データを反復処理するケースです。

あなたの場合、次のようなことをします:

var item = items.MaxBy(x => x.Height);

これは、Mehrdad の 2 番目のソリューション (基本的には と同じ) 以外の、ここで提示されたどのソリューションよりも優れています (IMO MaxBy):

  • これは、反復ごとに最大値を見つける以前に受け入れられた回答とは異なり、O(n)です (O(n^2) になります)。
  • 順序付けの解は O(n log n) です
  • 値を取得し、そのMax値を持つ最初の要素を見つけるのは O(n) ですが、シーケンスを 2 回繰り返します。可能であれば、LINQ をシングルパス方式で使用する必要があります。
  • 集計バージョンよりも読みやすく理解しやすく、要素ごとに 1 回だけ射影を評価します。
于 2009-07-09T05:32:44.710 に答える
228

これにはソート(O(n log n))が必要ですが、非常に単純で柔軟性があります。もう1つの利点は、LINQtoSQLで使用できることです。

var maxObject = list.OrderByDescending(item => item.Height).First();

listこれには、シーケンスを1回だけ列挙できるという利点があることに注意してください。listそれまでの間変更されないかどうかは問題ではないかもしれませんがList<T>、任意IEnumerable<T>のオブジェクトについては問題になる可能性があります。シーケンスが異なる列挙で変更されないことを保証するものは何もないため、シーケンスを複数回実行するメソッドは危険である可能性があります(シーケンスの性質によっては非効率的です)。ただし、それでも大きなシーケンスには理想的なソリューションではありません。MaxObject並べ替えなどを一切行わずに1回のパスで実行できるアイテムのセットが多数ある場合は、独自の拡張機能を手動で作成することをお勧めします(O(n)):

static class EnumerableExtensions {
    public static T MaxObject<T,U>(this IEnumerable<T> source, Func<T,U> selector)
      where U : IComparable<U> {
       if (source == null) throw new ArgumentNullException("source");
       bool first = true;
       T maxObj = default(T);
       U maxKey = default(U);
       foreach (var item in source) {
           if (first) {
                maxObj = item;
                maxKey = selector(maxObj);
                first = false;
           } else {
                U currentKey = selector(item);
                if (currentKey.CompareTo(maxKey) > 0) {
                    maxKey = currentKey;
                    maxObj = item;
                }
           }
       }
       if (first) throw new InvalidOperationException("Sequence is empty.");
       return maxObj;
    }
}

そしてそれを一緒に使用します:

var maxObject = list.MaxObject(item => item.Height);
于 2009-07-09T04:33:13.097 に答える
164

注文を行ってから最初のアイテムを選択すると、最初のアイテムの後にアイテムを注文するのに多くの時間が無駄になります。それらの順序は気にしません。

代わりに、集計関数を使用して、探しているものに基づいて最適なアイテムを選択できます。

var maxHeight = dimensions
    .Aggregate((agg, next) => 
        next.Height > agg.Height ? next : agg);

var maxHeightAndWidth = dimensions
    .Aggregate((agg, next) => 
        next.Height >= agg.Height && next.Width >= agg.Width ? next: agg);
于 2009-07-09T05:13:46.337 に答える
39

そして、これを試してみませんか??? :

var itemsMax = items.Where(x => x.Height == items.Max(y => y.Height));

またはさらに最適化:

var itemMaxHeight = items.Max(y => y.Height);
var itemsMax = items.Where(x => x.Height == itemMaxHeight);

うーん ?

于 2011-06-13T12:37:05.733 に答える
33

これまでの答えは素晴らしいです!しかし、次の制約があるソリューションが必要だと思います。

  1. プレーンで簡潔な LINQ;
  2. O(n) の複雑さ;
  3. プロパティを要素ごとに複数回評価しないでください。

ここにあります:

public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
    return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
}

public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
    return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
}

使用法:

IEnumerable<Tuple<string, int>> list = new[] {
    new Tuple<string, int>("other", 2),
    new Tuple<string, int>("max", 4),
    new Tuple<string, int>("min", 1),
    new Tuple<string, int>("other", 3),
};
Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4
于 2015-08-06T19:33:07.113 に答える
2

MAXを取得したい列で並べ替えてから、最初の列を取得すると機能すると思います。ただし、同じMAX値を持つオブジェクトが複数ある場合は、1つだけが取得されます。

private void Test()
{
    test v1 = new test();
    v1.Id = 12;

    test v2 = new test();
    v2.Id = 12;

    test v3 = new test();
    v3.Id = 12;

    List<test> arr = new List<test>();
    arr.Add(v1);
    arr.Add(v2);
    arr.Add(v3);

    test max = arr.OrderByDescending(t => t.Id).First();
}

class test
{
    public int Id { get; set; }
}
于 2009-07-09T04:36:55.053 に答える
1

Cameron の最初の回答に基づいて、SilverFlow ライブラリの FloatingWindowHost の拡張バージョンに追加したものを次に示します ( http://clipflair.codeplex.comソース コードの FloatingWindowHost.cs からコピー) 。

    public int MaxZIndex
    {
      get {
        return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
          int w = Canvas.GetZIndex(window);
          return (w > maxZIndex) ? w : maxZIndex;
        });
      }
    }

    private void SetTopmost(UIElement element)
    {
        if (element == null)
            throw new ArgumentNullException("element");

        Canvas.SetZIndex(element, MaxZIndex + 1);
    }

上記のコードに関して、Canvas.ZIndex はさまざまなコンテナーの UIElements で使用できる添付プロパティであり、Canvas でホストされている場合にのみ使用されることに注意してください (「Canvas コントロールを使用せずに Silverlight でレンダリング順序 (ZOrder) を制御する」を参照)。このコードを適応させることで、UIElement の SetTopmost および SetBottomMost 静的拡張メソッドを簡単に作成できると思います。

于 2013-12-06T13:15:56.137 に答える
1

Mehrdad Afshari のソリューションを、拡張メソッドをより高速な (そして見栄えの良い) ものに書き直すことでアップグレードすることもできます。

static class EnumerableExtensions
{
    public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
    {
        var enumerator = container.GetEnumerator();
        if (!enumerator.MoveNext())
            throw new ArgumentException("Container is empty!");

        var maxElem = enumerator.Current;
        var maxVal = valuingFoo(maxElem);

        while (enumerator.MoveNext())
        {
            var currVal = valuingFoo(enumerator.Current);

            if (currVal.CompareTo(maxVal) > 0)
            {
                maxVal = currVal;
                maxElem = enumerator.Current;
            }
        }

        return maxElem;
    }
}

そして、それを使用してください:

var maxObject = list.MaxElement(item => item.Height);

その名前は、C++ を使用している人には明らかです (そこに std::max_element があるため)。

于 2014-12-27T22:53:05.967 に答える