2012-04-03 25 views
7

Eventuali duplicati:
Natural Sort Order in C#ordinamento stringa numeri

Ho una lista con un sacco di numeri in esso. Ma sono salvati come stringhe a causa di alcune lettere aggiuntive.

Mia lista simile a questa:

1 
10 
11 
11a 
11b 
12 
2 
20 
21a 
21c 
A1 
A2 
... 

ma dovrebbe assomigliare a questa

1 
2 
10 
11a 
11b 
... 
A1 
A2 
... 

Come faccio a ordinare la mia lista per ottenere questo risultato?

+0

Rompere il 'numero' in componenti, quindi ordinare da questo. – leppie

+1

sì, ordinamento naturale è ciò che ur dopo. questo è un duplicato come dichiarato da Jon. Un buon articolo su http://zootfroot.blogspot.com.au/2009/09/natural-sort-compare-with-linq-orderby.html –

+0

[Natural Sorting in C#] (http: //www.interact -sw.co.uk/iangblog/2007/12/13/natural-sorting) –

risposta

12

Andando dai commenti precedenti, vorrei anche implementare una classe personalizzata IComparer<T> . Da quello che posso raccogliere, la struttura degli elementi è un numero, una combinazione di un numero seguito da una o più lettere. In questo caso, dovrebbe essere implementata la seguente implementazione IComparer<T>.

public class CustomComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     var regex = new Regex("^(d+)"); 

     // run the regex on both strings 
     var xRegexResult = regex.Match(x); 
     var yRegexResult = regex.Match(y); 

     // check if they are both numbers 
     if (xRegexResult.Success && yRegexResult.Success) 
     { 
      return int.Parse(xRegexResult.Groups[1].Value).CompareTo(int.Parse(yRegexResult.Groups[1].Value)); 
     } 

     // otherwise return as string comparison 
     return x.CompareTo(y); 
    } 
} 

Con questo IComparer<T>, sarete in grado di ordinare l'elenco dei string facendo

var myComparer = new CustomComparer(); 
myListOfStrings.Sort(myComparer); 

Questo è stato testato con i seguenti elementi:

2, 1, 4d, 4e, 4c, 4a, 4b, A1, 20, B2, A2, a3, 5, 6, 4f, 1a

e dà il risultato:

1, 1a, 2, 20, 4a, 4b, 4c, 4d, 4e, 4f, 5, 6, A1, A2, a3, B2

+0

Questo è bello, ma non gestisce correttamente cose come i numeri di versione ... 1.5.2, 1.9.9, 1.10.17 Stranamente, Windows Explorer ordina questa roba correttamente ... ma a quanto pare il comparatore non è disponibile per nessun altro codice C# per riprodurre lo stesso ordine di nome file che File Explorer mostrerà quando si ordina per nome file. Frustrante. – pmbAustin

+0

Credo che il Regex dovrebbe essere '^ (\\ d +)'. Ancora meglio, il Regex potrebbe essere semplicemente '(\\ d +)' per gestire 'Region 1, Region 10, Region 2' e' 1, 10, 2'. – stack247

2

Bene, è necessario estrarre il numero da ciascuna stringa e quindi ordinare l'elenco di stringhe in base all'elenco di numeri come chiavi. Fai questo in due passi.

Per estrarre il numero da ciascuna stringa, il modo più semplice per utilizzare un'espressione regolare: cercare una corrispondenza per (\d+) (se si dispone di numeri decimali o negativi, sarà necessario utilizzare un'espressione regolare diversa) . Diciamo che hai fatto che in una funzione chiamata ExtractNumber

Ora è possibile utilizzare alcuni LINQ creativo per ordinare, in questo modo:

strings.Select(s=>new { key=ExtractNumber(s), value=s }) // Create a key-value pair 
     .OrderBy(p=>p.key)        // Sort by key 
     .Select(p=>p.Value);        // Extract the values 
+0

Questa sembra una soluzione elegante con LINQ, ma cosa succederebbe quando l'iterazione arrivasse agli elementi 'A1' e' A2'? – Richard

+0

int.Parse ((nuovo Regex (@ "(? <= Pdf _) \ d *? (? = _ \. Bmp $)")). Match (file) .Value); // corrisponde pdf_123456890_.bmp –

0

Sono piuttosto nuovo in C#, ma qui è una soluzione che apprezzo in Java: è necessario procedere in 2 passaggi, prima definire un IComparer personalizzato e poi utilizzarlo per chiamare il metodo di ordinamento. Così si dovrebbe essere in grado di fare qualcosa di simile:

public class MyListSorter : IComparer<MyObject> 
{ 
    public int Compare(MyObject obj1, MyObject obj2) 
    { 
    if (!Char.IsNumber(obj1) && Char.IsNumber(obj2)) 
    { 
     return 0; 
    } 
    else if (Char.IsNumber(obj1) && !Char.IsNumber(obj2)) 
    { 
     return 1; 
    } 
    else 
    { 
     return obj2.CompareTo(obj1); 
    } 
    } 
} 

e poi

myObjectList.Sort(new MyListSorter()); 

altre informazioni su IComparer: http://support.microsoft.com/kb/320727

1

Poiché questo include molte operazioni con le stringhe, regex ecc., Non penso che sia un algoritmo efficiente ma sembra funzionare.

List<string> list1 = new List<string>() { "11c22", "1", "10", "11", "11a", "11b", "12", "2", "20", "21a", "21c", "A1", "A2" }; 
List<string> list2 = new List<string>() { "File (5).txt", "File (1).txt", "File (10).txt", "File (100).txt", "File (2).txt" }; 
var sortedList1 = NaturalSort(list1).ToArray(); 
var sortedList2 = NaturalSort(list2).ToArray(); 

public static IEnumerable<string> NaturalSort(IEnumerable<string> list) 
{ 
    int maxLen = list.Select(s => s.Length).Max(); 
    Func<string, char> PaddingChar = s => char.IsDigit(s[0]) ? ' ' : char.MaxValue; 

    return list 
      .Select(s => 
       new 
       { 
        OrgStr = s, 
        SortStr = Regex.Replace(s, @"(\d+)|(\D+)", m => m.Value.PadLeft(maxLen, PaddingChar(m.Value))) 
       }) 
      .OrderBy(x => x.SortStr) 
      .Select(x => x.OrgStr); 
} 
+0

Mi piacerebbe vedere una versione che gestiva effettivamente i numeri di versione ... che hanno più numeri da tenere in ordine: 2.5.7, 10.3.2, 2.18.3, ecc. – pmbAustin

+1

funziona perfettamente! grazie! –

Problemi correlati