2010-10-03 22 views
6

Tengo una consulta en LinqToSql que devuelve un LabelNumber:C# LINQ orden personalizado

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

var q entonces se convierte en un IEnumerable<string> con elementos como este:

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

Básicamente quiero ordenar los elementos como aparecen arriba, pero no puedo usar el OrderBy(x=>x.LabelNumber) porque "10" se colocará después de "1" y antes de "2".

Supongo que tengo que escribir una función de comparador personalizada, pero ¿cómo hago esto con linq?

Editar: Creo que todas las respuestas a continuación funcionarán, pero se debe agregar una advertencia a todas las respuestas.

Si está utilizando Linq2SQL no puede usar índices de matriz dentro de la consulta. Para superar esto, debe tener dos consultas. Uno que lee de SQL. El segundo hace el pedido:

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

Para su información, ordenar los números de la manera que desee se denomina "clasificación natural". – mquander

Respuesta

9

Es probable que no tiene que escribir un comparador personalizado. Si todas sus etiquetas están en el formulario number.letter, puede usar esto.

Si necesita más control, siempre se puede convertir los campos sortBy (a y b) a los tipos apropiados en lugar de enteros y cadenas.


Si esto es LINQ-to-SQL, esto realmente no funcionará ya que algunos métodos utilizados aquí no son compatibles. Aquí hay una versión amigable LINQ-to-SQL. No producirá la consulta más bonita, pero funcionará.

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

Recibo este error cuando ejecuto esto: 'System.InvalidOperationException: Nodo de expresión no reconocido: ArrayIndex' – Shawn

+0

Esto está en LINQ-to-SQL ¿verdad? Por supuesto, esto no funcionaría allí y requeriría algunos ajustes. –

+0

Agregué una edición que aborda el ajuste. Gracias. – Shawn

1

Si está seguro de que q es un formato correcto y se secuenciaron:

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

Está asumiendo que solo los números están desordenados. ¿Qué pasaría si tuvieras 2.B, 2,2.A? –

+0

@Michael: El OP no dijo que quiere ordenar por las letras ... y soy bastante flojo. De todos modos, es bastante probable que él lo quiera. LOL. –

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

donde AlphanumComparator es el excelente Alphanum natural sort algorithm por David Koelle. No hay necesidad de reinventar la rueda.

Si vas a utilizar la versión C# cambiarlo a:

AlphanumComparator : IComparer<string> 

y

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

+1, para presentarme a AlphanumComparator –

+0

Esta sintaxis no funciona. 'Argumento 3: no se puede convertir de 'AlphanumComparator' a 'System.Collections.Generic.IComparer ' ' – Shawn

+0

Eso es porque OrderBy espera un IComparer genérico, mientras que AlphanumComparator no es genérico. El cambio es bastante fácil de hacer, sin embargo. Agregue el en las declaraciones, cambie todos los objetos a Ts y elimine los moldes. – Kyte

0

Aquí está mi contribución .. utilizando Regular Expression y 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(); 

salida :

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

'Método 'System.Text.RegularExpressions.Match Match (System.String, System.String)' no tiene traducción soportada a SQL. Aunque parece funcionar en su ejemplo. – Shawn

+0

luego haz una 'lista' con tus números de etiquetas y luego ordena esa usando la expresión anterior. –

+0

Lo tengo resuelto. Gracias, conseguí tu código ejecutándose con Linq2SQL. No estaba al tanto de la limitación de la matriz. Terminé usando una versión más simple, ya que la expresión regular complica mi situación. El anidamiento secundario eventualmente se volverá más complicado y tendré que recurrir a tu solución. Gracias. – Shawn