2015-05-14 189 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