web-dev-qa-db-ja.com

特定のプロパティの最大値を持つオブジェクトを返すようにLINQを取得するにはどうすればよいですか?

次のようなクラスがある場合:

public class Item
{
    public int ClientID { get; set; }
    public int ID { get; set; }
}

そして、それらのアイテムのコレクション...

List<Item> items = getItems();

LINQを使用して、最も高いIDを持つ単一の「アイテム」オブジェクトを返すにはどうすればよいですか?

私が次のようなことをした場合:

items.Select(i => i.ID).Max(); 

実際に返されるものが最も高いIDを持つItemオブジェクト自体である場合にのみ、最も高いIDを取得します。 intではなく、単一の「Item」オブジェクトを返すようにします。

126
FrankTheTank

これは1回だけループします。

Item biggest = items.Aggregate((i1,i2) => i1.ID > i2.ID ? i1 : i2);

ニックに感謝-これが証拠だ

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<Item> items1 = new List<Item>()
        {
            new Item(){ ClientID = 1, ID = 1},
            new Item(){ ClientID = 2, ID = 2},
            new Item(){ ClientID = 3, ID = 3},
            new Item(){ ClientID = 4, ID = 4},
        };
        Item biggest1 = items1.Aggregate((i1, i2) => i1.ID > i2.ID ? i1 : i2);

        Console.WriteLine(biggest1.ID);
        Console.ReadKey();
    }


}

public class Item
{
    public int ClientID { get; set; }
    public int ID { get; set; }
}  

リストを並べ替えて同じ結果を得ます

137
Seattle Leonard
.OrderByDescending(i=>i.id).Take(1)

パフォーマンスの問題に関しては、この方法は線形アプローチよりも理論的に遅い可能性が非常に高いです。ただし、実際には、ほとんどの場合、違いを生じさせるのに十分な大きさのデータセットを扱っていません。

パフォーマンスが主な関心事である場合、シアトルレナードの答えは時間の直線的な複雑さを与えるはずです。または、一定の時間に最大値アイテムを返す別のデータ構造から開始することも検討できます。

51
Codism
int max = items.Max(i => i.ID);
var item = items.First(x => x.ID == max);

これは、もちろんitemsコレクションに要素があることを前提としています。

31
Nick Larsen

MaxBymorelinq プロジェクトから使用します。

items.MaxBy(i => i.ID);
29
tzaman

MoreLINQを使用せず、線形時間を取得する場合は、Aggregateを使用することもできます。

var maxItem = 
  items.Aggregate(
    new { Max = Int32.MinValue, Item = (Item)null },
    (state, el) => (el.ID > state.Max) 
      ? new { Max = el.ID, Item = el } : state).Item;

これは、匿名型の現在の最大要素(Item)と現在の最大値(Item)を記憶しています。次に、Itemプロパティを選択します。これは確かに少しいので、MaxBy拡張メソッドにラップして、MoreLINQと同じものを取得できます。

public static T MaxBy(this IEnumerable<T> items, Func<T, int> f) {
  return items.Aggregate(
    new { Max = Int32.MinValue, Item = default(T) },
    (state, el) => {
      var current = f(el.ID);
      if (current > state.Max) 
        return new { Max = current, Item = el };
      else 
        return state; 
    }).Item;
}
5
Tomas Petricek

または、独自の拡張メソッドを作成できます。

static partial class Extensions
{
    public static T WhereMax<T, U>(this IEnumerable<T> items, Func<T, U> selector)
    {
        if (!items.Any())
        {
            throw new InvalidOperationException("Empty input sequence");
        }

        var comparer = Comparer<U>.Default;
        T   maxItem  = items.First();
        U   maxValue = selector(maxItem);

        foreach (T item in items.Skip(1))
        {
            // Get the value of the item and compare it to the current max.
            U value = selector(item);
            if (comparer.Compare(value, maxValue) > 0)
            {
                maxValue = value;
                maxItem  = item;
            }
        }

        return maxItem;
    }
}
4
ollb

これを試して:

var maxid = from i in items
            group i by i.clientid int g
            select new { id = g.Max(i=>i.ID }
2
Pranay Rana

これは、@ Seattle Leonardの答えから派生した拡張メソッドです。

 public static T GetMax<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
 {
     return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
 }
2
Paul Richards

LINQでは、次の方法で解決できます。

Item itemMax = (from i in items
     let maxId = items.Max(m => m.ID)
     where i.ID == maxId
     select i).FirstOrDefault();
1
Ohlin

キャプチャされた変数を使用できます。

Item result = items.FirstOrDefault();
items.ForEach(x =>
{
  if(result.ID < x.ID)
    result = x;
});
1
Amy B