如何使用C#在数字子字符串上排序字符串列表
我有一个字符串列表,每个字符串包含一个数字子字符串,我想根据该子字符串的数值重新排序。 该集合看起来像这样,但更大:
List strings= new List { "some-name-(1).jpg", "some-name-(5).jpg", "some-name-(5.1).jpg", "some-name-(6).jpg", "some-name-(12).jpg" };
数字将始终用括号括起来,括号是字符串中唯一的括号,因此使用String.IndexOf
是可靠的。 请注意,不仅可能存在缺失的数字,而且还可能存在小数,而不仅仅是整数。
我很难获得在该子字符串的数值上排序的那些相同字符串的重新排序列表。 有没有人有办法做到这一点,希望有一个表现良好的方法? 谢谢。
这将检查括号之间的项是否可转换为double
,如果不是,则返回-1。
var numbers = strings.Select( x => x.Substring( x.IndexOf( "(" ) + 1, x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ).Select( x => { double val; if( double.TryParse( x, out val ) ) { return val; } // Or whatever you want to do return -1; } ).OrderBy( x => x ); // Or use OrderByDescending
如果您确定括号之间总会有数字,那么请使用它,因为它更短:
var numbers = strings.Select( x => x.Substring( x.IndexOf( "(" ) + 1, x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ) .Select( x => double.Parse(x)) .OrderBy( x => x ); // Or use OrderByDescending
编辑
我需要原始字符串,只需按这些数字排序。
基本上你需要做的是将谓词传递给OrderBy
并告诉它按数字排序:
var items = strings.OrderBy( x => double.Parse( x.Substring( x.IndexOf( "(" ) + 1, x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ));
OO方法怎么样?
我们正在订购字符串,但我们需要像数字一样对待它们。 如果我们可以通过一种方式调用OrderBy
并为我们订购它,那会不会很好? 好吧有。 OrderBy
方法将使用IComparable
如果有)。 让我们创建一个类来保存我们的jpg路径并实现IComparable
接口。
public class CustomJpg : IComparable { public CustomJpg(string path) { this.Path = path; } public string Path { get; private set; } private double number = -1; // You can even make this public if you want. private double Number { get { // Let's cache the number for subsequent calls if (this.number == -1) { int myStart = this.Path.IndexOf("(") + 1; int myEnd = this.Path.IndexOf(")"); string myNumber = this.Path.Substring(myStart, myEnd - myStart); double myVal; if (double.TryParse(myNumber, out myVal)) { this.number = myVal; } else { throw new ArgumentException(string.Format("{0} has no parenthesis or a number between parenthesis.", this.Path)); } } return this.number; } } public int CompareTo(CustomJpg other) { if (other == null) { return 1; } return this.Number.CompareTo(other.Number); } }
上面的方法有什么OrderBy
,如果我们继续调用OrderBy
,它不必每次都搜索开头(
和结尾)
并进行数字解析。 它在第一次调用时缓存它,然后继续使用它。 另一个好处是我们可以绑定到Path
属性,也可以绑定到Number
(我们必须从private更改访问修饰符)。 我们甚至可以引入一个新属性来保存缩略图并将其绑定到该图像。 正如您所看到的,这种方法更加灵活,简洁且采用OO方法。 此外,找到数字的代码在一个地方,所以如果我们从()
切换到另一个符号,我们只需在一个地方更改它。 或者我们可以修改为先查找()
,如果没有找到则查找另一个符号。
这是用法:
List jpgs = new List { new CustomJpg("some-name-(1).jpg"), new CustomJpg("some-name-(5).jpg"), new CustomJpg("some-name-(5.1).jpg"), new CustomJpg("some-name-(6).jpg"), new CustomJpg("some-name-(12).jpg") }; var ordered = jpgs.OrderBy(x => x).ToList();
您可以将此方法用于任何对象。
在上面的示例代码中,返回按数字排序的数字列表,但如果您想要按名称排序的文件名列表更好,则将相同的零添加到数字的开头,如“some-name-(001).jpg”和你可以简单地订购
List strings = new List { "some-name-(001).jpg", "some-name-(005.1).jpg", "some-name-(005).jpg", "some-name-(004).jpg", "some-name-(006).jpg", "some-name-(012).jpg" }; var orederedByName =strings.Select(s =>s ).OrderBy(s=>s);
如果您首先从右括号")"
开始切断零件,则可以更容易地选择子串。 即,从"some-name-(5.1).jpg"
你首先得到"some-name-(5.1"
。然后取出"("
之后的部分)。这节省了长度计算,因为第二Substring
自动将所有内容都取消字符串的结尾。
strings = strings .OrderBy(x => Decimal.Parse( x.Substring(0, x.IndexOf(")")) .Substring(x.IndexOf("(") + 1) ) ) .ToList();
这在这里可能不是很重要,但通常, decimal
存储数字以十进制表示法给出的数字比double
精度数字更准确。 double
可以将17.2
转换为17.19999999999999
。