2010-10-03 32 views
6

ho una query in LinqToSql che restituisce un LabelNumber:C# LINQ Ordinamento personalizzato

var q = from list in db.Lists 
     select list.LabelNumber; 

var q poi diventa un IEnumerable<string> con elementi come questo:

{"1","2","2.A","2.B","3","3.A","3.B"} 

Io fondamentalmente voglio ordinare gli elementi come appaiono sopra, ma non posso usare il OrderBy(x=>x.LabelNumber) perché "10" si posizionerebbe dopo "1" e prima di "2".

Suppongo di dover scrivere una funzione di confronto personalizzata, ma come faccio a farlo con linq?

Modifica: Penso che tutte le risposte seguenti funzioneranno, ma è necessario aggiungere un avvertimento a tutte le risposte.

Se si utilizza Linq2SQL non è possibile utilizzare gli indici di array all'interno della query. Per superare questo, dovresti avere due domande. Uno che legge da SQL. Il secondo fa l'ordine:

var q = from list in db.Lists 
      select list.LabelNumber; 

var q2 = q.AsEnumerable() 
      .OrderBy(x => int.Parse(x.LabelNumber.Split('.')[0])) 
      .ThenBy(x => x.Number 
         .Contains(".") ? 
           x.LabelNumber.Split('.')[1].ToString() 
           : 
           string.Empty); 
+0

FYI, l'ordinamento dei numeri nel modo desiderato è chiamato "tipo naturale". – mquander

risposta

9

Probabilmente non è necessario scrivere un operatore di confronto personalizzato. Se tutte le etichette sono nel formato number.letter, è possibile utilizzare questo.

Se avete bisogno di un maggiore controllo, si può sempre convertire i campi sortby (a e b) ai tipi appropriati, piuttosto che interi e stringhe.


Se questo è LINQ to SQL, questo in realtà non funziona in quanto alcuni metodi usati qui non sono supportati. Ecco una versione amichevole da LINQ a SQL. Non darà la domanda più bella, ma funzionerà.

var query = from list in db.Lists 
      let dot = list.LabelNumber.IndexOf('.') 
      let name = list.LabelNumber 
      let order = dot == -1 
       ? new { a = Convert.ToInt32(name.Substring(0, dot)), b = String.Empty } 
       : new { a = Convert.ToInt32(name.Substring(0, dot)), b = name.Substring(dot+1) } 
      orderby order.a, order.b 
      select list.LabelNumber; 
+0

Viene visualizzato questo errore quando si esegue questa operazione: 'System.InvalidOperationException: nodo di espressione non riconosciuto: ArrayIndex' – Shawn

+0

Questo è in LINQ-to-SQL, giusto? Naturalmente questo non avrebbe funzionato lì e avrebbe richiesto qualche ritocco. –

+0

Ho aggiunto una modifica che risolve il problema. Grazie. – Shawn

1

Se si è certi che q è ben formattato e sequenziati:

var result = q.OrderBy(x => int.Parse(x.Split('.')[0])); 
+2

Supponi che solo i numeri siano fuori servizio. E se avessi 2.B, 2,2.A? –

+0

@ Michael: L'OP non ha detto che vuole ordinare le lettere ... e sono abbastanza pigro. Ad ogni modo è abbastanza probabile che lo voglia. LOL. –

11
OrderBy(x=>x.LabelNumber, new AlphanumComparator()) 

dove AlphanumComparator è l'eccellente Alphanum natural sort algorithm da David Koelle. Non c'è bisogno di reinventare la ruota.

Se hai intenzione di utilizzare la versione C# modificarla in:

AlphanumComparator : IComparer<string> 

e

public int Compare(string x, string y) 
+3

+1, per avermi presentato a AlphanumComparator –

+0

Questa sintassi non funziona. 'Argomento 3: impossibile convertire da 'AlphanumComparator' a 'System.Collections.Generic.IComparer '' – Shawn

+0

Questo è perche 'OrderBy si aspetta un generico IComparer, mentre AlphanumComparator non è generico. Il cambiamento è abbastanza facile da fare, però. Aggiungi lo nelle dichiarazioni, cambia tutti gli oggetti in Ts e rimuovi i cast. – Kyte

0

Ecco il mio contributo .. usando Regular Expression e LAMBDA expression

List<String> Lst = new List<string> { "1", "2", "2.A","10.A", "2.C", "3", "3.A", "3.B","2.B","11.D" }; 
Lst = Lst.Select(X => new 
     { 
      Number = int.Parse(Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[1].Value), 
      Strings=Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[2].Value, 
      OriginalString = X 
     }).OrderBy(X => X.Number).ThenBy(X=>X.Strings) 
     .Select(X => X.OriginalString).ToList(); 

uscita :

"1" 
"2" 
"2.A" 
"2.B" 
"2.C" 
"3" 
"3.A" 
"3.B" 
"10.A" 
"11.D" 
+0

'Metodo 'System.Text.RegularExpressions.Match Match (System.String, System.String)' non ha traduzione supportata in SQL. Anche se sembra funzionare nel tuo esempio. – Shawn

+0

quindi crea un 'elenco' con i tuoi numeri di etichetta e poi riordinalo usando l'espressione sopra. –

+0

Hai risolto. Grazie, ho ottenuto il codice in esecuzione con Linq2SQL. Non ero a conoscenza della limitazione dell'array. Ho finito per utilizzare una versione più semplice, in quanto regex over complica la mia situazione. La sub nidificazione alla fine diventerà più complicata e dovrò ricorrere alla soluzione. Grazie. – Shawn

Problemi correlati