2012-01-06 12 views
5

Decir que tengo el siguiente:elemento de Max/Min sin utilizar IComparable <T>

public Class BooClass 
{ 
    public int field1; 
    public double field2; 
    public DateTime field3; 
} 

public List<BooClass> booList; 

Así, por ejemplo ¿Cómo consigo el elemento con el tiempo más temprano en field3 usando booList.Find()

Editar disculpas, quise hacer públicos todos los campos para simplificar el ejemplo. Sé que puedo hacerlo en Linux, me pregunto si hay una sola condición de línea simple para el método Find.

Respuesta

8

F # tiene útil minBy y maxBy operadores, que me gusta de implementar como métodos de extensión de C#, ya que la biblioteca de LINQ los omite. Es un poco de trabajo, pero sólo un poco, y que le permite evitar expresiones complejas tales como

var earliest = booList.First(b => b.Field3 == booList.Min(e => e.Field3)); 

En cambio, puede escribir esto:

var earliest = booList.MinBy(b => b.Field3); 

Una aplicación sencilla:

static T MinBy<T, C>(this IEnumerable<T> sequence, Func<T, C> keySelector) 
{ 
    bool first = true; 
    T result = default(T); 
    C minKey = default(C); 
    IComparer<C> comparer = Comparer<C>.Default; //or you can pass this in as a parameter 

    foreach (var item in sequence) 
    { 
     if (first) 
     { 
      result = item; 
      minKey = keySelector.Invoke(item); 
      first = false; 
      continue; 
     } 

     C key = keySelector.Invoke(item); 
     if (comparer.Compare(key, minKey) < 0) 
     { 
      result = item; 
      minKey = key; 
     } 
    } 

    return result; 
} 

Esto también es algo más eficiente que el complejo de expresión en la parte superior, ya que MinBy itera la secuencia exactamente una vez, mientras que las iteraciones de expresión más de una vez y de menos de o igual a dos veces. Y, por supuesto, la clasificación y después de tomar el primer elemento requiere de clasificación, que es O (n log n), mientras que esto es sólo O (n).

+0

¡Excelente! Y si usa un campo en particular varias veces para Mín. O Máx., Simplemente agregar una propiedad sería más simple que la implementación de IComparable , que por supuesto solo puede implementarse para un valor –

+0

Para un problema tan simple que ofrece n líneas de código no es bueno, no es óptimo, legible y tampoco está relacionado con la complejidad del problema –

+2

@SaeedAmiri pero el MinBy El método está pensado como un método de biblioteca, en otra clase, simplificando muchos códigos de cliente a expensas de crear un método separado y más complejo. Es por eso que tenemos métodos en primer lugar. Permiten la descomposición, duplicación reducida/reutilización de código , y la ocultación de los detalles de implementación, entre otras ventajas. ¿Cómo es 'var early = booList.MinBy (b => b.Field3);' no legible? – phoog

2

El enfoque O (n) es el siguiente. En primer lugar encontramos la fecha min (para field3), y luego encontrar primer objeto con esta fecha min:

var minDate = booList.Min(x=>x.field3); 
var item = booList.First(x=>x.field3 == minDate); 

Simplemente haga su propiedad pública.

+0

Así que podría ser: –

+0

BooClass earliest = booList.First (i => i.field3 == booList.Min (j => j.field3)); // Creo que esta es la sintaxis más corta, pero se tarda más tiempo que los métodos de LINQ? –

+0

@RichOliver, ¿cuál es el beneficio de una sintaxis más corta?mi código se ejecuta en O (n) tu sintaxis o la respuesta de Jason se ejecuta en O (n^2) entonces ¿por qué debería ofrecer una respuesta más lenta? También puedo escribir esto en una sola consulta, pero para hacerlo más legible lo escribí en dos líneas. –

5

Tendrá que exponer a través de field3 través de una propiedad pública (lo llamaremos Field3), pero se puede usar esto:

var earliest = booList.First(b => b.Field3 == booList.Min(e => e.Field3)); 

Tome un vistazo a Enumerable.First y Enumerable.Min

NOTA: Esto tiene una complejidad temporal de O (n^2) (tiempo cuadrático) porque atraviesa la lista a través de Min en cada iteración. Una colección suficientemente grande verá problemas graves de rendimiento en comparación con Saeed Amiri's answer, que se ejecuta en O (n) (tiempo lineal).

+0

Vaya, saltó el arma. Lo arreglaré ahora. –

+0

Disculpa que fue un descuido al no hacer públicos los campos. –

3

Uso OrdenarPor a continuación, obtener el primer elemento

var result = booList.OrderBy(p => p.field3).FirstOrDefault(); 
+0

dknaack respondió de manera similar, ¿por qué agregar esto después de 10 minutos? –

+0

Lo siento, no sé el límite de tiempo. Acabo de verlo ahora ... :( – shenhengbin

+2

Similar, pero no es igual. Me gusta mejor esta sintaxis. –

0

Por lo que yo puedo decir, no hay manera de recuperar el objeto BooClass con la fecha mínima sólo por el uso List<T>.Find. Por supuesto, usted puede hacer esto:

void Main() 
{ 
    List<BooClass> booList = new List<BooClass> { 
         new BooClass { field3 = DateTime.MaxValue}, 
         new BooClass { field3 = DateTime.Now }, 
         new BooClass { field3 = DateTime.MinValue }}; 
    var pred = GetPredicate(booList); 
    var result = booList.Find(pred); 
} 

public Predicate<BooClass> GetPredicate(List<BooClass> boos) 
{ 
    var minDate = boos.Min(boo => boo.field3); 
    return bc => bc.field3 == minDate; 
} 

(el cual - al igual que la solución de Saeed - también tiene O (n) la complejidad del tiempo), pero supongo que eso sería considerado hacer trampa ...

Cuestiones relacionadas