用字符串中的数字对选择列表项进行排序

时间:2019-02-20 19:39:50

标签: c# .net

我有一个要从枚举中提取的字符串列表,我需要按字母顺序排序,然后按数字排序,有些字符串没有数字,这些字符串应该在列表的末尾。

List =     
    "Incl 11
     Incl 12
     Excl 4
     Incl 3
     Other
     Incl 4
     Incl 10
     Incl 11
     Excl 10
     Incl 1
     Incl 2
     Withdrew Col
     Excl 1
     Excl 2
     Excl 3         
     Follow Up
    "

到目前为止,我已经知道了,但是它只是按数字排序,而不是按字母顺序排序,任何帮助将不胜感激。

    var test =  Enum.GetValues(typeof(Reason))
        .Cast<Reason>()
        .Select(sc => new SelectListItem { Value = ((int)sc).ToString(), Text = sc.GetDisplayName() })
        .OrderBy(s => s.Text, new MyNumberComparer())
        .ToList();

    class MyNumberComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            var xResultString = Regex.Match(x, @"\d+").Value;
            var yResultString = Regex.Match(y, @"\d+").Value;

            int xVal, yVal;
            var xIsVal = int.TryParse(xResultString, out xVal);
            var yIsVal = int.TryParse(yResultString, out yVal);

            if (xIsVal && yIsVal)   
                return xVal.CompareTo(yVal);
            if (!xIsVal && !yIsVal) 
                return x.CompareTo(y);
            if (xIsVal)             
                return -1;
            return 1;              
        }
    }

编辑最终输出应为:

List =     
    "Excl 1
     Excl 2
     Excl 3 
     Excl 4
     Excl 10
     Incl 1
     Incl 2
     Incl 3
     Incl 4
     Incl 10
     Incl 11
     Incl 12
     Follow Up       
     Other                         
     Withdrew Col                        
    "

3 个答案:

答案 0 :(得分:1)

您快到了。如果值的非数字部分不相同,则仅需要比较非数字部分。只需将这些行放在比较方法的开头即可。

        var xstring = Regex.Match(x, @".+?(?=\d+)").Value; //extract non-digit part
        var ystring = Regex.Match(y, @".+?(?=\d+)").Value; //extract non-digit part

        if (!string.IsNullOrEmpty(xstring) && !string.IsNullOrEmpty(ystring))
        {
            var comp = xstring.CompareTo(ystring);
            if (comp != 0)
            {
                return comp;
            }
        }

答案 1 :(得分:0)

以下是IComparer的实现,它将为您带来想要的东西

class MyNumberComparer : IComparer<string> {
    public static Tuple<string, int?> SplitString(string s) {
        var x = s.LastIndexOf(' ');
        return x == -1 || x == s.Length - 1 || !int.TryParse(s.Substring(x + 1), out int n) ?
            Tuple.Create(s, (int?)null) : Tuple.Create(s.Substring(0, x), (int?)n);
    }

    public int Compare(string str1, string str2) {
        if (string.Compare(str1, str2) == 0)
            return 0;

        var str1Items = SplitString(str1);
        var str2Items = SplitString(str2);

        var prefixCompare = string.Compare(str1Items.Item1, str2Items.Item1);

        if (str1Items.Item2 == null && str1Items.Item2 == null)
            return prefixCompare;

        if (str1Items.Item2 == null)
            return 1;

        if (str2Items.Item2 == null)
            return -1;

        return prefixCompare == 0 ? (int)(str1Items.Item2 - str2Items.Item2) : prefixCompare;
    }
}

我已经根据您在问题中提到的问题(以空格分隔的数字)为基础,但是您可以更改SplitString的实现,以反映将传入的字符串分解为字符串和数字或null的情况。

如果您希望以特殊方式(例如不区分大小写的方式)进行字符串比较,则可以更新string.Compare代码中的MyNumberComparer.Compare调用以反映出来。

答案 2 :(得分:0)

这是一个基于正则表达式组的简单解决方案。经过测试,它可以为您提供所需的结果。绝对不漂亮:

class Comparer : IComparer<string> {
    static Regex Matcher = new Regex(@"([^\d]+)(\d+)");

    public int Compare(string x, string y) {
        var xMatch = Matcher.Match(x);
        var yMatch = Matcher.Match(y);

        if (xMatch.Success != yMatch.Success)
            return xMatch.Success ? -1 : 1;

        if (!xMatch.Success)
            return string.Compare(x, y);

        if (xMatch.Groups[1].Value != yMatch.Groups[1].Value)
            return string.Compare(xMatch.Groups[1].Value, yMatch.Groups[1].Value);

        return (int.Parse(xMatch.Groups[2].Value) - int.Parse(yMatch.Groups[2].Value)) < 0 ? -1 : 1;
    }
}

如果您在一个字符串中有多个数字,上帝会帮助您。在这种情况下,您必须更改正则表达式模式以在字符串末尾仅查找数字。抱歉,我对此很懒。