2009-04-24 28 views
11

Me enfrenta un problema extraño al ordenar una lista de cadenas con valores enteros. Sin embargo, algunos valores pueden tener como prefijo algunos caracteres.Cómo ordenar cadenas enteras?

p. Ej.

// B1, 5, 50, A10, 7, 72, B3, A1, A2 

Hay básicamente números de página y deben ser ordenados como:

// A1, A2, A10, B1, B3, 5, 7, 50, 72 

Pero si utilizo cadena por defecto de clasificación, entonces estos se ordenan como

// A1, A10, A2, B1, B3, 5, 50, 7, 72 

Cualquier solución para esto en DO#?

+0

Se puede usar esta 'NaturalStringComparer' que puse juntos y limpiado un poco (no recuerdo donde me dieron la base para ello) . Utiliza la función Win32 StrCmpLogicalW que menciona Skizz. http://my.opera.com/Svishy/blog/2009/03/02/natural-sorting – Svish

Respuesta

17

Está buscando Alphanum algorithm. Afortunadamente para ti, ya existen varias implementaciones. Ver here.

+0

Alphanum devolverá // 5, 7, 50, 72, A1, A2, A10, B1, B3 en lugar de // A1 ... 5 – Carra

+2

Si revisa algunos de los ejemplos del código, se describe cómo cambiarlo para acomodar escenarios sutilmente diferentes. –

0

Bien, siempre se puede invocar la función StrCmpLogicalW de la API de Win32, que hace exactamente lo que se desea (es lo que Explorer usa para clasificar nombres de archivo). El único inconveniente posible es que el tipo es insensible a las mayúsculas y minúsculas.

5

Esto es cómo lo resolví para nuestra aplicación, el orden será como en un directorio de Windows:

public class NaturalSortComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     return StrCmpLogicalW(x, y); 
    } 

    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)] 
    public static extern int StrCmpLogicalW(string x, string y); 
} 

Uso:

NaturalSortComparer comparer = new NaturalSortComparer(); 
    return comparer.Compare(string1, string2); 

Pero probablemente no es exactamente lo que quiere:

// A1, A2, A10, B1, B3, 5, 7, 50, 72

Esto dará

// 5, 7, 50, 72, A1, A2, A10, B1, B3

0
No

seguro sobre el rendimiento, y de que esto se puede optimizar, pero hace el trabajo:

string[] sort(string[] data) 
{ 
    return data 
     .OrderBy(s => Regex.Match(s, @"^\D").Length == 0) 
     .ThenBy(s => Regex.Match(s, @"\D*").Value) 
     .ThenBy(s => Int32.Parse(Regex.Match(s, @"\d+").Value)).ToArray(); 
} 

var result = sort(new string[] { "B1", "5", "50", "A10", "7", "72", "B3", "A1", "A2" }); 
3

Lo que estás buscando es un tipo natural.

Jeff Atwood hizo una gran publicación en su blog una vez, explicando el concepto y vinculando a varias otras fuentes con algoritmos que podría tomar como ejemplo.

Sorting for Humans : Natural Sort Order

1

Aquí es un comparador personalizado que va a clasificar en el orden requerido. Tenga en cuenta que no hay errores/verificaciones de cordura en este código: asume que todas las cadenas estarán en el formato correcto.

public class MyComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     Match xMatch = Regex.Match(x, @"^(\D*)(\d+)$"); 
     Match yMatch = Regex.Match(y, @"^(\D*)(\d+)$"); 

     string xChars = xMatch.Groups[1].Value; 
     string yChars = yMatch.Groups[1].Value; 

     if ((xChars.Length == 0) && (yChars.Length > 0)) 
     { 
      return 1; 
     } 
     else if ((xChars.Length > 0) && (yChars.Length == 0)) 
     { 
      return -1; 
     } 
     else 
     { 
      int charsResult = xChars.CompareTo(yChars); 

      return (charsResult != 0) 
       ? charsResult 
       : int.Parse(xMatch.Groups[2].Value) 
        .CompareTo(int.Parse(yMatch.Groups[2].Value)); 
     } 
    } 
} 

Se puede utilizar de este modo:

List<string> testList = 
    new List<string>() { "B1","5","50","A10","7","72","B3","A1","A2" }; 

testList.Sort(new MyComparer()); // A1, A2, A10, B1, B3, 5, 7, 50, 72 
Cuestiones relacionadas