2015-05-14 246 views
4

我有數組字符串列表:按數字順序排列數組列表然後按字母排序

"1A", "12A", "12B", "6", "A", "5B", "B", "13"

如果我做myList.Sort();然後我得到:

"1A", "12A", "12B", "13", "5B", "6", "A", "B"

但我需要的是在前面的數字先排序,然後按字母:

"1A", "5B", "6", "12A", "12B", "13", "A", "B"

我可以用

public class CustomComparer : IComparer 
{ 
    Comparer _comparer = new Comparer(System.Globalization.CultureInfo.CurrentCulture); 

    public int Compare(object x, object y) 
    { 
     // Convert string comparisons to int 
     return _comparer.Compare(Convert.ToInt32(x), Convert.ToInt32(y)); 
    } 
} 

但它拋出異常。我如何得到我所需要的?

+2

「它會拋出異常」是遠遠不夠的信息。什麼是例外? –

+1

由於某些字符串包含字母,因此如果不首先刪除字母,則無法將它們轉換爲「int」。 – juharr

+0

什麼是您的'CurrentCulture'? –

回答

5

您不能簡單地將字符串"1A""5B"轉換爲Convert.ToInt32(x),因爲它的一部分不是Int32的一部分。

相反,您應該首先將字符串拆分爲兩部分 - 數字和其他所有內容,然後與tiebreak進行比較。

一個使用LINQ的OrderByThenBy這樣做會寫兩個輔助方法,然後方式:

static int ExtractPrefix(string s) { 
    // Parse the digits and stop; return the number 
} 
static string ExtractSuffix(string s) { 
    // Skip digits, and return everything else 
} 
... 
var sorted = unsorted.OrderBy(ExtractPrefix).ThenBy(ExtractSuffix).ToList(); 
+0

查找數字與字符串其餘部分之間的「邊界線」可以用許多不同的方法完成。獨立完成是一個很好的練習。 – dasblinkenlight

+0

我試過我得到錯誤'System.Collections.ArrayList'不包含'OrderBy'的定義,並且沒有找到接受'System.Collections.ArrayList'類型的第一個參數的擴展方法'OrderBy' – babboon

+2

@babboon您需要在代碼頂部添加'using System.Linq',並且切換到'System.Collections.Generic.List ',或者在'unsorted'和''之間添加'.Cast ()'' .OrderBy()',即'unsorted.Cast ().OrderBy(ExtractPrefix).ThenBy(ExtractSuffix).ToList();' – dasblinkenlight

-1

下面執行比較器的應符合你的要求:

public int Compare(string x, string y) 
{ 
    var rx = new Regex("^(d+)"); 

    var xRes = rx .Match(x); 
    var yRes = rx .Match(y); 

    if (xRes.Success 
     && yRes.Success) 
    { 
     return int.Parse(xRes.Groups[1].Value). 
       CompareTo(int.Parse(yRes.Groups[1].Value)); 
    } 

    return x.CompareTo(y); 
} 
+1

這就是說「123ABC」相當於「123XYZ」。解析數字部分後,如果值相同,則必須比較阿爾法部分。 – juharr

+0

它也不適用於例子中的「A」。 –

1

給這個實現一個試試?

http://www.dotnetperls.com/alphanumeric-sorting

從上面的鏈接:

public class AlphanumComparatorFast : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
    string s1 = x as string; 
    if (s1 == null) 
    { 
     return 0; 
    } 
    string s2 = y as string; 
    if (s2 == null) 
    { 
     return 0; 
    } 

    int len1 = s1.Length; 
    int len2 = s2.Length; 
    int marker1 = 0; 
    int marker2 = 0; 

    // Walk through two the strings with two markers. 
    while (marker1 < len1 && marker2 < len2) 
    { 
     char ch1 = s1[marker1]; 
     char ch2 = s2[marker2]; 

     // Some buffers we can build up characters in for each chunk. 
     char[] space1 = new char[len1]; 
     int loc1 = 0; 
     char[] space2 = new char[len2]; 
     int loc2 = 0; 

     // Walk through all following characters that are digits or 
     // characters in BOTH strings starting at the appropriate marker. 
     // Collect char arrays. 
     do 
     { 
     space1[loc1++] = ch1; 
     marker1++; 

     if (marker1 < len1) 
     { 
      ch1 = s1[marker1]; 
     } 
     else 
     { 
      break; 
     } 
     } while (char.IsDigit(ch1) == char.IsDigit(space1[0])); 

     do 
     { 
     space2[loc2++] = ch2; 
     marker2++; 

     if (marker2 < len2) 
     { 
      ch2 = s2[marker2]; 
     } 
     else 
     { 
      break; 
     } 
     } while (char.IsDigit(ch2) == char.IsDigit(space2[0])); 

     // If we have collected numbers, compare them numerically. 
     // Otherwise, if we have strings, compare them alphabetically. 
     string str1 = new string(space1); 
     string str2 = new string(space2); 

     int result; 

     if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) 
     { 
     int thisNumericChunk = int.Parse(str1); 
     int thatNumericChunk = int.Parse(str2); 
     result = thisNumericChunk.CompareTo(thatNumericChunk); 
     } 
     else 
     { 
     result = str1.CompareTo(str2); 
     } 

     if (result != 0) 
     { 
     return result; 
     } 
    } 
    return len1 - len2; 
    } 
} 
+1

只有鏈接的答案在SO中通常是不被接受的。作爲評論,這會更好。 –

+0

我在那裏複製了IComparer實現 – Shawn

+3

雖然這可能起作用,但它看起來比實際需要複雜得多。 –

5

你比較器過於簡單。您的比較需要將每個值分成數字和其他數字,然後先比較數字,然後再比較字符串是否相等。因此,這將是這樣的:

public sealed class NumberStringComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     return NumberString.Parse(x).CompareTo(NumberString.Parse(y)); 
    } 


    private struct NumberString : IComparable<NumberString> 
    { 
     private readonly int? number; 
     private readonly string text; 

     private NumberString(int? number, string text) 
     { 
      this.number = number; 
      this.text = text; 
     } 

     internal static NumberString Parse(string text) 
     { 
      // TODO: Find where the digits stop, parse the number 
      // (if there is one), call the constructor and return a value. 
      // (You could use a regular expression to separate the parts...) 
     } 

     public int CompareTo(NumberString other) 
     { 
      // TODO: Compare numbers; if they're equal, compare 
      // strings 
     } 
    } 
} 

如果你有任何的待辦事項的問題(花一些時間嘗試後),你可以要求更具體的幫助 - 但,這是一般的方法,我想使用。

+0

@Johnbot:是的,會解決的。 –

3

試試這個:

public class CustomComparer : IComparer<string> { 
    Comparer _comparer = new Comparer(System.Globalization.CultureInfo.CurrentCulture); 

    public int Compare(string x, string y) { 
    string numxs = string.Concat(x.TakeWhile(c => char.IsDigit(c)).ToArray()); 
    string numys = string.Concat(y.TakeWhile(c => char.IsDigit(c)).ToArray()); 

    int xnum; 
    int ynum; 
    if (!int.TryParse(numxs, out xnum) || !int.TryParse(numys, out ynum)) { 
     return _comparer.Compare(x, y); 
    } 
    int compareNums = xnum.CompareTo(ynum); 
    if (compareNums != 0) { 
     return compareNums; 
    } 
    return _comparer.Compare(x, y); 
    } 
} 
+0

錯誤'string'不包含'TakeWhile'的定義,並且沒有可以找到接受'string'類型的第一個參數的擴展方法'TakeWhile' – babboon

+1

add using System.Linq;在文件的頂部。 –

+0

對不起,我的不好:) – babboon