2010-02-17 20 views
5

Digamos que tengo una lista de objetos:pedido selectiva con LINQ, ordenación parcial

var items = new { 
    new { Order = 0 }, 
    new { Order = 1 }, 
    new { Order = -1 }, 
    new { Order = 3 }, 
    new { Order = 2 }, 
    new { Order = -1 } 
}; 

tengo que pedirlo por lo que los artículos con "Orden> -1" estar en la cima de la lista ordenada por la Orden ascendiendo, y los elementos restantes con "Orden == -1" los seguían.

¿Existe una forma más elegante de hacer esto que el uso de ConAct() + Donde() cláusulas:

var orderedItems = items.Where(x => x.Order > -1).OrderBy(x => x.Order) 
        .Conact(items.Where(x => x.Order == -1); 

modo que después de clasificar esta lista se vería así:

var items = new { 
    new { Order = 0 }, 
    new { Order = 1 }, 
    new { Order = 2 }, 
    new { Order = 3 }, 
    new { Order = -1 }, 
    new { Order = -1 } 
}; 

también " la lista de elementos "en el escenario real ya es un objeto IQueryable complejo. Es por eso que estoy tratando de encontrar la forma más óptima de hacer un pedido tan selectivo.

+1

Algo confundido ahora ... ascendente significaría 1,2,3 ¿no es así? EN tal orden, -1 debe ser automáticamente antes de 0? – flq

+3

Debería usar 'Concat', no' Union'. – SLaks

+0

SLaks: Buen punto –

Respuesta

7

Usted podría intentar esto - que produce el resultado que espera:

items.OrderBy(x.Order => x.Order == -1).ThenBy(x.Order => x.Order); 
3

Si ordena por ascensión, -1 ya debería estar en la parte superior de la lista, porque es el valor más pequeño.

Sin embargo, en general, si aún desea aplicar una clasificación diferente a subconjuntos de datos, no creo que haya una manera más elegante, porque eso es exactamente lo que está haciendo y la unión es lógicamente precisa. Está tratando de sacar dos subconjuntos separados de los datos, ordenarlos de manera diferente y luego fusionarlos, que es una de las cosas para lo que se usará la unión.

0

Utilice un custom comparer para definir el orden que desee.

public class MyComparer : IComparer<int> 
{ 
    public int Compare(int a, int b) 
    { 
     if ((a < 0) && (b >= 0)) 
     { 
      return 1; 
     } 
     if ((a >= 0) && (b < 0)) 
     { 
      return -1; 
     } 
     return int.Compare(a, b); 
    } 
} 

entonces se podría hacer:

var orderedItems = items.OrderBy(x => x.Order, new MyComparer()); 
+0

Al volver a leer la pregunta, no está claro qué hacer con otros números negativos en absoluto. Esta respuesta debería dar la idea general, sin embargo. –

6

Como se mencionó Mike, en su ejemplo, que iba a funcionar automáticamente, pero decir que queríamos conseguir todos los elementos primero y -1 a continuación, ordenar los elementos restantes en una orden descendiente. Esto se puede hacer usando un buen truco. Puede usar múltiples claves al ordenar elementos. La primera clave puede ser un valor booleano que será false para todos los valores -1 (para que sean los primeros) y true para todos los demás valores (para que no se reordenarán). La segunda clave puede ser lo que quieras para pedir los elementos restantes. Por ejemplo:

var nums = new int[] { -1, 4, 2, 3, -1, 4, 7 }; 
var q = from n in nums 
     orderby n != -1, n descending 
     select n; 

Será primera ceder todos los valores para los cuales es n != -1false y luego todos los elementos ordenados utilizando n descending de manera que obtendrá:

-1, -1, 7, 4, 4, 3, 2 

Esto funciona en general cuando se necesita para manejar algunos elementos, especialmente en el orden, solo debe proporcionar las claves de pedido correctas.

+0

Parece exactamente lo que estaba buscando. Probando ... –

+0

Muy bien, pero tu explicación me confunde ... si! = -1 los elementos son editados primero, ¿no deberían venir -1 elementos al final, o qué está pasando exactamente aquí? – flq

+0

Esto se debe al orden ascendente de los valores booleanos. 'true.CompareTo (false)' da 1, que indica que 'true> false', por lo que el orden ascendente es' false, true' (y por lo tanto los elementos 'false' son los primeros). –

1
OrderBy(x => x.Order < 0 ? int.MaxValue : x.Order) 

o si necesita pedir los valores negativos en orden descendente

OrderBy(x => x.Order < 0 ? (long)int.MaxValue - x.Order : (long)x.Order) 
+1

Esto devolvería los elementos negativos desordenados. No está claro a partir de la pregunta si eso es lo que quiere. –

+1

Bueno, él no lo pidió :) Añadiré una adaptación simple. – leppie

1

también un comparador costumbre aquí si quiere que -1 aparezca primero, pero el resto sea descendiendo, pero de alguna manera lo encuentro más elegante :) Tenga en cuenta que está hecho para ints

class Comparer : IComparer<int> 
{ 
    public int Compare(int x, int y) 
    { 
    if (x == -1 || y == -1) return x - y; 
    return y - x; 
    } 
}