按带有小数的字母数字排序列表

3
我有一个需要按字母数字排序的 List<string>,但其中还包含了小数。示例如下:
E11.9 E13.9 E10.9 E11.65 E10.65 E11.69 E13.10 E10.10
我需要的输出应该像这样: E10.10 E10.65 E10.9 E11.69 E11.9 等等...
我尝试了以下代码:
result.Sort((s1, s2) =>
{
    string pattern = "([A-Za-z])([0-9]+)";
    string h1 = Regex.Match(s1, pattern).Groups[1].Value;
    string h2 = Regex.Match(s2, pattern).Groups[1].Value;
    if (h1 != h2)
        return h1.CompareTo(h2);
    string t1 = Regex.Match(s1, pattern).Groups[2].Value;
    string t2 = Regex.Match(s2, pattern).Groups[2].Value;
    return int.Parse(t1).CompareTo(int.Parse(t2));
});

但是它似乎只按字母顺序排序,然后按小数点前的数字排序。所以我得到了这个:
E10.9
E10.65
E10.10
E11.9
E11.69
等等...
我在正则表达式中漏掉了什么吗?还是有更好的方法来实现这一点?

它是否总是以 E 开头,或者至少总是以单个字母开头? - Tim Schmelter
不总是 E,但总是一个单字母。 - Kieran Quinn
1
“.”(点)符号不在[0-9]区间内。因此,您需要按E排序,然后再按点号前的数字排序。 - Grundy
而且你应该使用double.Parse而不是int。使用适当的CultureInfo小数分隔符。 - Ilia Maskov
5个回答

6

你能做到这个吗?:

测试数据:

var ls=new List<string>
    {
    "E11.9",
    "E13.9",
    "E10.9",
    "E11.65",
    "E10.65",
    "E11.69",
    "E13.10",
    "E10.10",
    };

Linq:

var result= ls
             .OrderBy (l =>l.Substring(0,1))
             .ThenBy(l =>double.Parse(l.Substring(1), CultureInfo.InvariantCulture))
             .ToList();

2
这只有在每行开头都是相同的字母时才有效,对吧?我猜为了适应这种情况,你需要先按 l.SubString(0,1) 排序,然后再按 ThenBy(l => double.Parse [...] ) 排序。 - germi
文化十进制分隔符怎么样? - Ilia Maskov
1
@agent5566:我已经更新答案,包括十进制分隔符。 - Arion
有人发布了这个答案,但后来可能删除了它。不过它似乎很有效:result = result.OrderBy(x => x.PadRight(10)).ToList(); - Kieran Quinn
1
@KieranQuinn 这个解决方案对于 "E13.9" 和 "E9.0" 是错误的。 - Grundy
显示剩余2条评论

2
Arion的回答的基础上:
var result = list.OrderBy(l => l[0]).ThenBy(l => double.Parse(l.Substring(1));

首先,您需要按字母排序,然后按字母后面的数字排序。如果您需要处理不同的文化设置,请在 double.Parse 中提供相应的信息。请参阅 MSDN 上的 Double.Parse

l.Substring(0,1)l[0] 是一样的吗? :-) - Grundy
1
伙计们,如果程序在小数分隔符不同的文化中运行,例如“。”和“,”,那么double.Parse将会出错。 - Ilia Maskov
@agent5566 没错,我会在回答中加上一些关于这个的内容,谢谢。 - germi

1
这是一个没有正则表达式的LINQ解决方案:

var ordered = from str in result
              let firstChar = string.IsNullOrEmpty(str) ? "" : str.Substring(0, 1)
              let decimalPart = string.IsNullOrEmpty(str) ? "" : str.Substring(1)
              let numOrNull = decimalPart.TryGetDecimal(NumberFormatInfo.InvariantInfo)
              orderby firstChar, numOrNull ?? decimal.MaxValue ascending, str ascending
              select str;
result = ordered.ToList();

使用此扩展程序将子字符串解析为十进制数:

public static decimal? TryGetDecimal(this string item, IFormatProvider formatProvider = null)
{
    if (formatProvider == null) formatProvider = NumberFormatInfo.CurrentInfo;
    decimal d = 0m;
    bool success = decimal.TryParse(item, NumberStyles.Any, formatProvider, out d);
    if (success) 
        return d;
    else
        return null;
}

您应该在LINQ查询中使用此扩展,而不是本地变量,如此处所学。


1
如果格式为[单个字母][小数],您可以执行以下操作:
var result= list.
              .OrderBy(s => s.Substring(0,1))
              .ThenBy(s => double.Parse(s.Substring(1), CultureInfo.InvariantCulture));

无需将正则表达式纳入故事中


0

另一种带有更新正则表达式的变体

ls.Sort((a, b) =>
{
    var pattern = new Regex(@"(?<letter>[A-Za-z])(?<number>[0-9]+\.?[0-9]*)$");
    var matchA = pattern.Match(a);
    var matchB = pattern.Match(b);
    var compareLetter = matchA.Groups["letter"].Value.CompareTo(matchB.Groups["letter"].Value);
    if (compareLetter != 0) return compareLetter;

    return double.Parse(matchA.Groups["number"].Value, CultureInfo.InvariantCulture).CompareTo(
            double.Parse(matchB.Groups["number"].Value, CultureInfo.InvariantCulture)
           );
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接