2012-02-29 20 views
5

tengo la clase myItem con 3 propiedades como abajo:LINQ: Agrupación por y seleccionar de una lista de objetos basado en el valor máximo

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; } } 

    // ... 
} 

También tengo una lista de myItem de la siguiente manera:

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"), 
    // ... 
} 

¿cómo puedo GROUP BY myList para que me quede SOLO con los artículos que tienen Maximum TimeStamp? es decir, el resultado a continuación:

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

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

Gracias de antemano.

+0

Me tomé la libertad de editar su código, espero no haber estropeado su ejemplo – BlackBear

Respuesta

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

Esto seleccionará sus resultados en el menor tiempo posible (al menos que yo puedo imaginar) sin crear nuevos objetos y iterar sobre la colección la menor cantidad de veces.

+0

Exactamente lo que necesitaba. En la parte de los registros que he sacado de la base de datos con fechas coincidentes y diferentes valores, quería tomar el valor más alto. Tu hombre rock gracias. –

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

esto no compila mate. – MaYaN

+0

Olvidé el "into g". Necesitas aprender linq si quieres usar, compañero. –

3

Seleccione el Max del Grupo:

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 - Estás en lo cierto. La instrucción select de la consulta crea un * new * 'MyItem' y usa un [object initializer] (http://msdn.microsoft.com/en-us/library/bb397680.aspx) para establecer las propiedades. –

+0

@ChristopherCurrens - TNX para la aclaración, sin embargo, como puede ver en mi reciente edición anterior, mis propiedades son getters solamente así que no puedo establecerlas aquí y realmente no quiero crear nuevos objetos. – MaYaN

+0

@ ALIR.Bousari - Ya veo. Bueno, entonces voy a recuperar mi respuesta. Es más complicado que estos, por eso lo eliminé, pero no crea nuevos objetos. –

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

¡Fantástico! justo lo que quería. muchas gracias :-) – MaYaN

+1

@ ALIR.Bousari - Si bien esta respuesta produce el resultado esperado, es un algoritmo * muy * lento: O (n^2). Cometí la velocidad de ejecución con la respuesta de @ JoeTuskan con esta, con * solo * 1000 elementos en la lista ejecutando la consulta 5 veces. Esta respuesta tomó '00: 00: 15.8860736' mientras que Joe solo tomó' 00: 00: 00.0080155'. –

+0

@ Christopher, gracias por el análisis que seguramente tendrá impacto a medida que la lista se agranda (que es mi caso), no pude implementar la solución de Joe ya que no puedo entender la sección SELECCIONAR – MaYaN

0

bien, me hizo cambiar de clase MyItem por alguna conveniencia LINQ (espero que esto no causa problemas) mediante la adición de un constructor en blanco a la clase:

public MyItem() { }

En un programa de consola de ejemplo , este código funcionará:

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(); 
} 

El resultado coincide con los resultados esperados.

Cuestiones relacionadas