2015-09-07 10 views
5

Per ordinare un elenco con Linq, è necessario chiamare OrderBy per prima cosa chiamare ThenBy sul risultato per gli ordini subordinati.elenco ordini in cui l'ordine di livello superiore è sconosciuto

Sono in una situazione in cui non conosco l'ordinamento di livello superiore prima mano. Ho una lista di ordini che dovrebbero essere applicati in modo condizionale.

Ti piace questa:

var list = new List<Tuple<int, string, DateTime>>(); 
list.Add(new Tuple<int, string, DateTime>(1, "B", new DateTime(2020, 1, 1))); 
list.Add(new Tuple<int, string, DateTime>(2, "A", new DateTime(2000, 1, 1))); 
list.Add(new Tuple<int, string, DateTime>(3, "C", new DateTime(1900, 1, 1))); 

var orderedList = list; 

if (sortByString) 
{ 
    orderdedList = orderedList.ThenBy(listItem => listItem.Item2); 
} 

if (sortByDateTime) 
{ 
    orderedList = orderedList.ThenBy(listItem => listItem.Item3); 
} 

orderList = orderedList.ThenBy(listItem => listItem.Item1); 

Così la lista sarà sempre ordinate per Item1, e condizionatamente da Item2 e/o Item3 prima.

Come realizzare questo in C#? Anche le soluzioni senza Linq sono benvenute.

+1

cosa c'è di sbagliato con quello che hai - basta scrivere 'var = orderedlist list.OrderBy (t => t.Item1);' e conserva tutto ** ma ** l'ultima riga – Carsten

+0

'ThenBy' è disponibile solo su' IOrderedEnumerable', quindi è possibile utilizzare un controllo del tipo. Se 'IOrderedEnumerable' usa' ThenBy' else 'OrderBy' – Jehof

+2

@Carsten Il problema è che Johan vuole che l''ordine1' ordina * l'ultimo *. Non è esattamente facile da leggere, ma è quello che ha scritto :) – Luaan

risposta

7

Basta usare

var orderedItems = list.OrderBy(_ => 1); 

Questo vi dà l'ordinamento di default (non), e consente di aggiungere come molti altri gli ordini che vuoi in seguito utilizzando solo ThenBy.

EDIT:

Come osservato Tim, questo prevede il trasporto di una penalizzazione delle prestazioni - sembra che il default LINQ to Objects provider non è abbastanza intelligente per ricostruire l'ordine di sbarazzarsi del "non -ordering". Questo non è un problema se la tua lista è piccola, ma se impiega una quantità di tempo non trascurabile, probabilmente vorrai farlo nel modo più difficile.

Ad esempio, è possibile utilizzare un metodo di supporto come

public static IEnumerable<T> AppendOrdering<T, U>(this IEnumerable<T> @this, 
                Func<T, U> selector) 
{ 
    if (@this is IOrderedEnumerable<T>) return @this.ThenBy(selector); 

    return @this.OrderBy(selector); 
} 

Questo non è esattamente lo stesso si sta facendo, ma a meno che non si sta lavorando su un enumerabile che è stato ordinato prima, funzionerà allo stesso modo.

+0

Tecnicamente questo è _non_ un non-ordering ma un ordinamento con lo stesso valore costante. Dato che è lo stesso per ogni oggetto, viene utilizzato il primo 'ThenBy' e così via. Probabilmente micro-ottimizzando ma su grandi collezioni può fare la differenza.Perché usare uno pseudo-confronto se puoi iniziare con il giusto? Esempio: http://csharppad.com/gist/6d19c47c672c81dc2c52 (30 e 17 secondi sul mio pc) –

+0

@TimSchmelter Interessante, la differenza è molto più grande di quanto mi aspettassi (in SQL questo è un no-op, ovviamente). Sul mio computer, sto diventando 16s vs 12s - ancora piuttosto costoso se hai bisogno di lavorare con liste lunghe, ma non abbastanza terrificante come il tuo aumento del 100% in runtime. Ma forse ho solo una cache della CPU più grande di te. Sicuramente andrei con la tua soluzione (beh, dopo averla fatta funzionare come vuole l'OP: D). È solo una di quelle strane decisioni di design: capisco il perché, ma rende la composizione delle query un po 'più difficile di quanto dovrebbe essere. – Luaan

2

Utilizzare un IOrderedEnumerable invece di una lista e di un if ... else:

IOrderedEnumerable<Tuple<int, string, DateTime>> orderedItems = null; 

if (sortByDateTime) 
    orderedItems = list.OrderBy(listItem => listItem.Item3); 
else if (sortByString) 
    orderedItems = list.OrderBy(listItem => listItem.Item2); 

orderedItems = orderedItems.ThenBy(listItem => listItem.Item1); 
list = orderedItems.ToList(); 
+0

Si noti che le mie condizioni di ordinamento non erano un 'if {} else se {} 'ma' if {} if {} '. Se 'sortByString' e' sortByDateTime' sono veri allora il secondo orderby sovrascriverà il primo. –

+0

@JohanvanderSlikke: ok, non era chiaro. Modificato la mia risposta. Ma se 'sortByDateTime' ha la priorità dovresti usare' if (sortByDateTime) {} else if (sortByString) {} ' –

-1

è necessario utilizzare una macchina a stati come il codice qui sotto

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     enum State 
     { 
      One, 
      Two, 
     } 
     static void Main(string[] args) 
     { 
      State state = State.A; 

      switch (state) 
      { 
       case State.One: 
        //Order by item one 
        state = State.Two; 
        break; 
       case State.Two: 
        //Order by item two or three 
        state = State.One; 
        break; 
      } 
     } 
    } 
} 


​