2010-02-14 18 views
7

Supongamos que tengo una lista de elementos (por ejemplo, Publicaciones) y quiero encontrar el primer elemento de acuerdo con un orden no trivial (por ejemplo, PublishDate y luego CommentsCount como desempate). La forma natural de hacer esto con LINQ es así:¿Cómo encontrar el primer artículo según un pedido específico usando LINQ en O (n)?

posts.OrderBy(post => post.PublishDate).ThenBy(post => post.CommentsCount).First() 

Sin embargo, el micro-optimizador en mí está preocupado de que llamar OrdenarPor en realidad me O (n * LGN) cuesta para clasificar toda la lista, cuando todo Lo que realmente necesito es una operación O (n) find-minimum.

Entonces, ¿LINQ es lo suficientemente inteligente como para devolver algo de OrderBy() que sabe cómo optimizar las llamadas posteriores First()? De lo contrario, ¿cuál es la mejor manera de hacerlo desde el primer momento? (Siempre puedo escribir mi propia implementación FindMinimumItem, pero eso parece exagerado).

+0

si la clave es realmente tan impreso que no necesita el ThenBy pero podría crear una clave coumpund de los dos. Lo cual sería fácil ya que el primero es un largo (tic) o un fijo con una cuerda. y que de hecho sería O (n) que usted solicita, pero de nuevo no hay garantía de que O (n) sea más rápido que O (nlogn) –

Respuesta

2

La clasificación es inteligente en la forma en que sólo lo hará el ThenBy en el primer grupo de la OrderBy, pero el OrderBy todavía tiene que ordenar todos los elementos antes de que pueda devolver el primer grupo.

se puede utilizar el método agregado para obtener el primer puesto de acuerdo con una comparación personalizada: (. Suponiendo que está utilizando LINQ a objetos, por supuesto)

Post lowest = 
    posts.Aggregate((Post)null, 
    (x, y) => 
     x == null 
     || y.PublishDate < x.PublishDate 
     || (y.PublishDate == x.PublishDate && y.CommentsCount < x.CommentsCount) 
     ? y : x 
); 

+0

Gracias, esa es una buena solución aunque sea un poco detallada. – Avish

0

Normalmente es max o min (no sé cómo se llama en LinQ), dada una clave específica; clasificar y obtener el primero o el último parece excesivo en cualquier idioma o marco.

+0

Los métodos que toman claves en LINQ lamentablemente devuelven la clave máxima/mínima en sí misma en lugar del objeto que contiene esa clave. –

+0

¿no puedes hacer una clave compuesta entonces? En python es muy típico organizar una tupla temporal que contiene la clave y el registro completo, ya que se comparan lexicográficamente. – fortran

+1

Aunque .NET 4.0 tiene un tipo Tuple, .NET 3.5 no. Es posible que este enfoque funcione en .NET 4.0, pero no estoy seguro de que sea muy idiomático. –

2

¿Esto está en SQL o LINQ to Objects? Si es el último, probablemente desee MinBy de MoreLINQ; su declaración tal como está escrita la ordenará y luego tomará el primer elemento.

Y sí, es una lástima que no incluya esto (y cosas similares como DistinctBy) fuera de la caja.

EDIT: Veo que su pregunta ha cambiado; MásLINQ no admite una comparación compuesta como esa. En MiscUtil tengo un código para crear un compuesto IComparer<T> - puede pasarlo a MinBy usando la función de identidad como selector de llave. Agrega a una solicitud de función para un MinBy que tiene una fuente y un IComparer<T> sin un selector de llave :)

+0

Gracias, echaré un vistazo a estos. Es realmente una pena que no haya una manera predeterminada de convertir conjuntos de expresiones clave (por ejemplo, como se usa con OrderBy ... ThenBy) y un IComparer. – Avish

+1

Una alternativa a 'ThenBy' podría estar devolviendo una tupla:' Tuple.Create (post.PublishData, post.CommentsCount) '. –

Cuestiones relacionadas