2012-04-03 12 views
7

Powiel możliwe:
Natural Sort Order in C#sort-strunowe numery

Mam listę z wieloma numerami w nim. Są one jednak zapisywane jako ciągi znaków z powodu dodatkowych liter.

Moja lista wygląda tak:

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

ale powinno to wyglądać tak

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

Jak sortować moją listę, aby uzyskać ten wynik?

+0

Break 'numer' do komponentów, a następnie sortować według tego. – leppie

+1

tak, naturalny rodzaj jest po tym. jest to duplikat, jak stwierdził Jon. Dobry artykuł na ten temat na 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) –

Odpowiedz

12

Przechodzenie przez poprzednie komentarze, chciałbym również zaimplementować niestandardową klasę IComparer<T> . Z tego, co mogę zebrać, struktura pozycji jest albo liczbą, połączeniem liczby, po której następuje litera (y). W takim przypadku należy wykonać następującą implementację 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); 
    } 
} 

Dzięki tej IComparer<T>, będziesz w stanie uporządkować listę string wykonując

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

został przetestowany z następujących elementów:

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

i daje wynik:

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

+0

To jest fajne, ale nie obsługuje poprawnie takich rzeczy jak numery wersji ... 1.5.2, 1.9.9, 1.10.17 Dziwnie, Eksplorator Windows sortuje to poprawnie ... ale najwyraźniej porównywarka nie jest dostępna dla żadnego innego kodu C# w celu odtworzenia tej samej kolejności nazw plików, co Eksplorator plików pokaże przy sortowaniu według nazwy pliku. Denerwujący. – pmbAustin

+0

Wierzę, że Regex powinien być '^ (\\ d +)'. Co więcej, Regex może być po prostu '(\\ d +)' do obsługi 'Region 1, Region 10, Region 2' oraz' 1, 10, 2'. – stack247

2

Cóż, musisz wyodrębnić liczbę z każdego ciągu, a następnie posortować listę ciągów na podstawie listy cyfr jako kluczy. Zrób to w dwóch krokach.

Aby wyodrębnić liczbę z każdego ciągu, najprostszym sposobem jest użycie wyrażenia regularnego - poszukaj dopasowania dla (\d+) (jeśli masz numery ujemne lub dziesiętne, będziesz musiał użyć innego wyrażenia regularnego) . Powiedzmy, że to zrobiłeś w funkcji o nazwie ExtractNumber

Teraz można użyć jakiegoś twórczego LINQ do sortowania, tak:

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

To wygląda jak eleganckie rozwiązanie wykorzystujące LINQ, ale co by się stało, gdy iteracja trafi do pozycji "A1" i "A2"? – Richard

+0

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

0

jestem raczej nowy w C#, ale tutaj jest to rozwiązanie cenię w Java: musisz wykonać 2 kroki, najpierw zdefiniuj spersonalizowany program IComparer, a następnie użyj go podczas wywoływania metody sortowania. Więc powinieneś być w stanie zrobić coś takiego:

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); 
    } 
    } 
} 

a następnie

myObjectList.Sort(new MyListSorter()); 

Więcej informacje na IComparer: http://support.microsoft.com/kb/320727

1

Ponieważ zawiera wiele operacji łańcuchowych, wyrażeń regularnych itp., Nie sądzę, że jest to skuteczny algorytm, ale wygląda na to, że działa.

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

Chciałbym zobaczyć wersję, która faktycznie obsługuje numery wersji ... które mają wiele numerów do utrzymania w kolejności: 2.5.7, 10.3.2, 2.18.3, itp. – pmbAustin

+1

to działa świetnie! Dziękuję Ci! –