简体   繁体   English

按数字排序数组列表,然后按字母排序

[英]Sort array list by numbers then by letters

I have array list of strings: 我有字符串数组列表:

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

If I do myList.Sort(); 如果我做myList.Sort(); then I get: 然后我得到:

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

But what I need is first sort by numbers in front, then by letter: 但我需要的是先按照前面的数字排序,然后按字母排序:

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

I could use 我可以用

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

But it throws exception. 但它抛出异常。 How do I get what I need? 我如何得到我需要的东西?

Your comparer is too simplistic. 你的比较器太简单了。 Your comparison needs to split each value into the number and the rest, then compare the numbers first, then the strings if they're equal. 您的比较需要将每个值拆分为数字和其余值,然后首先比较数字,然后比较字符串是否相等。 So it would be something like: 所以它会是这样的:

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

If you have problems with either of the TODOs ( after spending some time trying), you can ask for more specific help - but this is the general approach I'd use. 如果您有一定的待办事项的问题(花一些时间尝试 ),你可以要求更具体的帮助-但,这是一般的方法我会使用。

You cannot simply pass a string "1A" or "5B" to Convert.ToInt32(x) , because it has a portion that is not part of an Int32 . 您不能简单地将字符串"1A""5B"传递给Convert.ToInt32(x) ,因为它的一部分不是Int32一部分。

Instead, you should first split the string into two parts - the digits and everything else, and do the comparison with tie breaks. 相反,您应该首先将字符串拆分为两部分 - 数字和其他所有部分,然后使用tie tie进行比较。

One way of doing it would be writing two helper methods, and then using LINQ's OrderBy and ThenBy : 一种方法是编写两个辅助方法,然后使用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();

Try this: 试试这个:

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

Give this implementation a try? 试试这个实现?

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

From the link above: 从上面的链接:

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

Below implementation of comparer should match your requirements: 以下比较器的实现应符合您的要求:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM