2012-02-29 14 views
5

mam klasę MyItem z 3 mieszkań, jak poniżej:LINQ: grupowanie Przez i wybranie pozycji z listy obiektów na podstawie wartości max

class MyItem 
{ 
    private string _name; 
    private int _value 
    private DateTime _TimeStamp; 

    public MyItem(string name, int value, string timeStamp) 
    { 
     this._name = name; 
     this._value = value; 
     this._timeStamp = DateTime.Parse(timeStamp); 
    } 

    public string Name 
    { get {return this_name; } } 

    public int Value 
    { get {return this._value; } } 

    public DateTime TimeStamp 
    { get {return this._timeStamp; } } 

    // ... 
} 

też mam listę MyItem jak poniżej:

var myItems = new List<MyItem>() { 
    new MyItem("A", 123, "23/02/2012"), 
    new MyItem("A", 323, "22/02/2012"), 
    new MyItem("B", 432, "23/02/2012"), 
    new MyItem("B", 356, "22/02/2012"), 
    // ... 
} 

W jaki sposób mogę ZAREZERWOWAĆ WEDŁUG myList, dzięki czemu JEDYNIE zostanę z przedmiotami, które mają Maximum TimeStamp? tj. wynik poniżej:

"A" 123 23/02/2012<br> 

"B" 432 23/02/2012<br> 

Z góry dziękuję.

+0

Mogłem edytować kod, mam nadzieję, że nie zawiodłem twojego przykładu – BlackBear

Odpowiedz

6
myItems.GroupBy(item => item.Name) 
     .Select(grp => grp.Aggregate((max, cur) => 
          (max == null || cur.Date > max.Date) ? cur : max)) 

To będzie wybrać swoje wyniki w jak najkrótszym czasie (przynajmniej, że mogę zrozumieć) bez tworzenia nowych obiektów i iteracji nad zbierać najmniejszą ilość razy.

+0

Dokładnie to, czego potrzebowałem. W parzystych rekordach, które wyciągnąłem z bazy danych o pasujących datach i różnych wartościach, chciałem przyjąć najwyższą wartość. Podziwiasz człowieka. –

0
var tmp = select i from myItems 
group i by i.Name into g 
select new MyItem 
{ 
    g.Name, 
    Value = g.OrderByDescending(x => x.Timestamp).First().Value, 
    Timestamp = g.Max(x => x.Timestamp) 
}; 
+0

to nie kompiluje mate. – MaYaN

+0

Zapomniałem "na g". Musisz nauczyć się linq, jeśli chcesz użyć, kolega. –

3

Wybierz Max z Grupy:

from item in MyItems 
group item by item.Name into grouped 
let maxTimeStamp = grouped.Max(i => i.TimeStamp) 
select grouped.First(i => i.TimeStamp == maxTimeStamp) 
+0

@ ALIR.Bousari - Masz rację. Wyrażenie select zapytania tworzy * nowy * 'MyItem' i używa [inicjatora obiektu] (http://msdn.microsoft.com/en-us/library/bb397680.aspx) do ustawienia właściwości. –

+0

@ ChristusCurrens - tnx dla wyjaśnienia jednak jak widać z mojej ostatniej edycji powyżej, moje właściwości są tylko pobierające, więc nie mogę ich tutaj ustawić i naprawdę nie chcę tworzyć nowych obiektów. – MaYaN

+0

@ ALIR.Bousari - Rozumiem. Cóż, wtedy cofnę odpowiedź. Jest to bardziej skomplikowane niż to, dlatego usunąłem go, ale nie tworzy on nowych obiektów. –

1
var temp = myItems.Where(x => x.TimeStamp == myItems.Where(y => y.Name == x.Name).Max(z => z.TimeStamp)).Distinct().ToList(); 
+0

Fantastyczne! właśnie tego chciałem. dziękuję bardzo :-) – MaYaN

+1

@ ALIR.Bousari - Podczas gdy ta odpowiedź daje oczekiwaną wydajność, jest to * bardzo * wolny algorytm: O (n^2). Porównałem prędkość wykonania z odpowiedzią @ JoeTuskan'a na tę, z * tylko * 1000 pozycji na liście wykonującej kwerendę 5 razy. Ta odpowiedź zajęła '00: 00: 15.8860736', podczas gdy Joe wziął tylko' 00: 00: 00.0080155'. –

+0

@ Christopher, dziękuję za analizę, która z pewnością wpłynie na to, że lista się powiększy (co jest moim przypadkiem), nie byłem w stanie wdrożyć rozwiązania Joego, ponieważ nie rozumiem sekcji SELECT – MaYaN

0

OK, zmieniłem swoją klasę MyItem jakiegoś wygody LINQ (nadzieja ta nie powoduje problemów) poprzez dodanie pustego konstruktora klasy:

public MyItem() { }

W programie konsoli próbki , ten kod będzie działał:

static void Main(string[] args) 
{ 
    var myItems = new List<MyItem>() 
    { 
     new MyItem("A", 123, "23/02/2012"), 
     new MyItem("A", 323, "22/02/2012"), 
     new MyItem("B", 432, "23/02/2012"), 
     new MyItem("B", 356, "22/02/2012") 
     // ... 
    }; 

    var grouped = from m in myItems 
        group m by m.Name into g 
        let maxTimestamp = g.Max(t => t.TimeStamp) 
        select new MyItem 
        { 
         Name = g.Key, 
         Value = g.First(f => f.TimeStamp == maxTimestamp).Value, 
         TimeStamp = maxTimestamp 
        }; 
    foreach (var gItem in grouped) 
    { 
     Console.WriteLine(gItem.Name + ", " + gItem.Value + ", " + gItem.TimeStamp); 
    } 

    Console.ReadLine(); 
} 

Dane wyjściowe odpowiadają oczekiwanym wynikom.

Powiązane problemy