2010-07-02 7 views
15

Ho bisogno di ordinare in memoria gli elenchi di stringhe o numeri in ordine ascendente o discendente. Tuttavia, l'elenco può contenere valori nulli e tutti i valori nulli devono essere visualizzati dopo i numeri o le stringhe.Come si può fare l'ordinamento personalizzato in LINQ con null sempre alla fine?

vale a dire i dati di input potrebbe essere:

1, 100, null, 5, 32.3

Il risultato ascendente sarebbero

1, 5, 32.3, 100, null

L'elenco discendente sarebbe

100, 32.3, 5, 1, null

Tutte le idee su come rendere questo lavoro?

risposta

6

È possibile scrivere il proprio comparatore che delega a uno esistente per non null, ma alla fine ordina sempre valori null. Qualcosa di simile a questo:

public class NullsLastComparer<T> : IComparer<T> 
{ 
    private readonly IComparer<T> proxy; 

    public NullsLastComparer(IComparer<T> proxy) 
    { 
     this.proxy = proxy; 
    } 

    public override int Compare(T first, T second) 
    { 
     if (first == null && second == null) 
     { 
      return 0; 
     } 
     if (first == null) 
     { 
      return 1; 
     } 
     if (second == null) 
     { 
      return -1; 
     } 
     return proxy.Compare(first, second); 
    } 
} 

EDIT: Alcuni problemi con questo approccio:

In primo luogo, esso non gioca bene con i tipi anonimi; potrebbe essere necessario un metodo di estensione separato per farlo funzionare bene. Oppure usa la risposta di Ken :)

Ancora più importante, viola il contratto IComparer<T>, che specifica che i valori nulli devono essere i primi. Ora personalmente, penso che questo sia un errore nella specifica IComparer<T> - dovrebbe forse definire che il comparatore dovrebbe gestire null, ma dovrebbe non specificare se vengono prima o l'ultima ... fa requisiti come questo (quale sono perfettamente ragionevoli) impossibili da adempiere con la massima chiarezza possibile, e ha ogni genere di conseguenze imbarazzanti per cose come un confronto inverso. Ci si aspetterebbe che una cosa del genere invertisse completamente l'ordine, ma secondo le specifiche dovrebbe comunque mantenere i valori nulli all'inizio :(

Non penso di aver visto alcuna implementazione di ordinamento .NET che effettivamente si basa su questo, ma è sicuramente vale la pena di essere a conoscenza di

+0

Non si tratta di una violazione del contratto con le implementazioni di ordinamento .NET? Ho pensato di aver letto da qualche parte che i metodi di ordinamento possono presupporre che i null vengano prima ... o ...? –

+0

@Lasse: Si tratta di una violazione dell'interfaccia 'IComparer , sì - modificherò il mio post per menzionarlo. –

+0

Sì, ma da quello che ricordo da quando ho letto su questo, sembrava che i metodi di ordinamento potessero prendere scorciatoie a causa di questo contratto, e quindi in realtà non chiamare il metodo di comparazione per alcuni casi, poiché "sapeva" già quale risultato sta per essere. In altre parole, non produrrà risultati funky in alcuni casi? –

38

non ho un compilatore di fronte a me per controllare, ma sto pensando qualcosa del tipo:.

x.OrderBy(i => i == null).ThenBy(i => i) 
+3

Nice ... questo è un approccio davvero dolce. Voterò per questo più di una volta se potessi ... –

+0

Molto bello. Questo approccio funziona anche per Linq-To-Entities e si traduce correttamente in Sql. –

0

come ha detto Jon, è necessario definisci il tuo Comparatore personalizzato, implementando IComparer. Ecco come il tuo metodo Compare nel comparatore personalizzato vorrebbe che potesse mantenere null alla fine.

public int Compare(Object x, Object y) 
    { 
     int retVal = 0; 

     IComparable valX = x as IComparable; 
     IComparable valY = y as IComparable; 

     if (valX == null && valY == null) 
     { 
      return 0; 
     } 

     if (valX == null) 
     { 
      return 1; 
     } 
     else if (valY == null) 
     { 
      return -1; 
     } 

     return valX.CompareTo(valY); 
    } 
Problemi correlati