2010-02-17 24 views
5

Diciamo che ho una lista di oggetti:ordinazione selettiva con LINQ, ordinamento parziale

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

ho bisogno di ordinare in modo che gli elementi con "Ordine> -1" essere in cima alla lista ordinata dal decreto ascendente e gli articoli rimanenti con "Ordine == -1" li stavano seguendo.

C'è un modo più elegante per fare questo che usare Conact() + Dove() clausole:

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

in modo che dopo l'ordinamento questa lista sarebbe simile:

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

anche " articoli "la lista nello scenario reale è già un oggetto IQueryable complesso. Ecco perché sto cercando di trovare il modo migliore di fare un ordine così selettivo.

+1

Abbastanza confuso ora ... l'ascesa significherebbe 1,2,3 non sarebbe? IN tale ordinamento, -1 dovrebbe essere automaticamente prima di 0? – flq

+3

Si dovrebbe usare 'Concat', non' Union'. – SLaks

+0

SLaks: Good point –

risposta

7

Si potrebbe provare questo - produce il risultato che si aspettano:

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

Se ordini in ordine crescente, -1 dovrebbe essere già in cima all'elenco, perché è il valore più piccolo.

Tuttavia, più in generale, se si desidera applicare un diverso ordinamento ai sottoinsiemi dei dati, non penso che ci sarebbe un modo più elegante, perché è esattamente quello che si sta facendo e l'unione è logicamente accurata. Stai cercando di estrarre due sottoinsiemi separati dei dati, ordinarli in modo diverso, e quindi unirli insieme, che è una delle cose per cui devo usare l'unione.

0

Utilizzare un custom comparer per definire l'ordine desiderato.

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

allora si potrebbe fare:

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

Dopo aver riletto la domanda, non è chiaro cosa fare con altri numeri negativi. Questa risposta dovrebbe dare l'idea generale, però. –

6

Come Mike accennato, nel tuo esempio, che avrebbe funzionato automaticamente, ma dire che abbiamo voluto ottenere tutti -1 primi elementi e gli elementi quindi ordinare rimanenti in un ordine decrescente. Questo può essere fatto usando un bel trucco. Puoi utilizzare più chiavi quando ordini elementi. La prima chiave può essere un valore booleano che sarà false per tutti i valori -1 (quindi saranno i primi) e true per tutti gli altri valori (quindi non verranno riordinati). La seconda chiave può essere qualsiasi cosa tu voglia ordinare gli elementi rimanenti. Per esempio:

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

Sarà prima produrrà tutti i valori per i quali è n != -1false e quindi tutti gli elementi ordinata utilizzando n descending così avrai:

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

Questo funziona in generale quando è necessario gestire alcuni elementi in particolare nell'ordinamento: è sufficiente fornire le giuste chiavi di ordinazione.

+0

Sembra esattamente quello che stavo cercando. Testing ... –

+0

Molto bello, ma la tua spiegazione mi confonde ... se! = -1 gli elementi vengono restituiti per primi, non dovrebbero esserci -1 elementi alla fine, o cosa sta succedendo esattamente qui? – flq

+0

Ciò è dovuto all'ordinamento crescente dei valori booleani. 'true.CompareTo (false)' fornisce 1, che indica che 'true> false', quindi l'ordine ascendente è' false, true' (e quindi gli elementi 'false' vengono prima). –

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

o se avete bisogno di ordinare i valori negativi in ​​ordine decrescente

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

Ciò restituirebbe gli elementi negativi non ordinati. Non è chiaro dalla domanda se è quello che vuole. –

+1

Beh, non l'ha chiesto :) Aggiungerò un semplice adattamento. – leppie

1

anche un operatore di confronto personalizzato qui se vuoi che prima appaia -1, ma il resto sia discendente, ma in qualche modo lo trovo più elegante :) Nota è fatto per inti

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