2009-04-24 17 views
11

Mam do czynienia z dziwnym problemem podczas sortowania listy ciągów z wartościami całkowitymi. Jednak niektóre wartości mogą być poprzedzone niektórymi znakami.Jak sortować ciągi całkowe?

np.

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

Istnieją numery stron i powinny być klasyfikowane następująco:

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

Ale jeśli mogę użyć domyślny ciąg sortowania a następnie zostaną one klasyfikowane jak

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

Każde rozwiązanie tego problemu w DO#?

+0

Można użyć tej 'NaturalStringComparer' że ułożyła i oczyścić trochę (nie pamiętam gdzie mam podstawę do niego) . Używa funkcji Win32 StrCmpLogicalW wspomnianej przez Skizz. http://my.opera.com/Svishy/blog/2009/03/02/natural-sorting – Svish

Odpowiedz

17

Poszukujesz modelu Alphanum algorithm. Na szczęście dla ciebie istnieje już kilka implementacji. Zobacz here.

+0

Alphanum zwróci // 5, 7, 50, 72, A1, A2, A10, B1, B3 zamiast // A1 ... 5 – Carra

+2

Jeśli przejrzysz kilka przykładów kodu, to opisałeś, jak to zmienić, aby dopasować się do różnych scenariuszy. –

0

Cóż, zawsze możesz zadzwonić do funkcji Win32 API StrCmpLogicalW, która robi dokładnie to, czego potrzebujesz (to jest to, czego Explorer używa do sortowania nazw plików). Jedynym możliwym minusem jest to, że sortowanie jest niewrażliwe na wielkość liter.

5

To jak ja rozwiązać go dla naszej aplikacji, kolejność będzie jak w katalogu 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); 
} 

Zastosowanie:

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

Ale to chyba nie jest to dokładnie to, co chcesz:

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

To da

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

0

Nie wiesz o wydajność, a na pewno może to być zoptymalizowane, ale to działa:

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

To, czego szukasz, to sortowanie naturalne.

Jeff Atwood napisał świetny post na swoim blogu, wyjaśniając koncepcję i łącząc z różnymi innymi źródłami za pomocą algorytmów, które można zastosować jako przykład.

Sorting for Humans : Natural Sort Order

1

Oto zwyczaj comparer że sortuje do swojej odpowiedniej kolejności. Należy zauważyć, że w tym kodzie nie ma sprawdzeń błędów/poprawności: zakłada on, że wszystkie ciągi będą miały poprawny format.

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

Można go używać tak:

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