How to order list of strings on number substring using C#

与世无争的帅哥 提交于 2019-12-08 09:29:00

问题


I have a list of strings, each containing a number substring, that I'd like to be reordered based on the numerical value of that substring. The set will look something like this, but much larger:

List<string> strings= new List<string>
{
    "some-name-(1).jpg",
    "some-name-(5).jpg",
    "some-name-(5.1).jpg",
    "some-name-(6).jpg",
    "some-name-(12).jpg"
};

The number will always be surrounded by parentheses, which are the only parentheses in the string, so using String.IndexOf is reliable. Notice that not only may there be missing numbers, but there can also be decimals, not just integers.

I'm having a really tough time getting a reordered list of those same strings that has been ordered on the numerical value of that substring. Does anyone have a way of doing this, hopefully one that performs well? Thanks.


回答1:


This will check if the items between the parenthesis is convertible to a double, if not it will return -1 for that case.

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

If you are sure there will always be a number between the parenthesis, then use this as it is shorter:

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

EDIT

I need the original strings, just ordered on those numbers.

Basically what you need to do is to pass a predicate to the OrderBy and tell it to order by the number:

var items = strings.OrderBy(
   x => double.Parse( x.Substring( x.IndexOf( "(" ) + 1, 
   x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ));

How about an OO approach?

We are ordering string but we need to treat them like numbers. Wouldn't it be nice if there was a way we can just call OrderBy and it does the ordering for us? Well there is. The OrderBy method will use the IComparable<T> if there is one. Let's create a class to hold our jpg paths and implement the IComparable<T> interface.

public class CustomJpg : IComparable<CustomJpg>
{
    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);
    }
}

What is nice about the above approach is if we keep calling OrderBy, it will not have to search for the opening ( and ending ) and doing the parsing of the number every time. It caches it the first time it is called and then keeps using it. The other nice thing is that we can bind to the Path property and also to the Number (we would have to change the access modifier from private). We can even introduce a new property to hold the thumbnail image and bind to that as well. As you can see, this approach is far more flexible, clean and an OO approach. Plus the code for finding the number is in one place so if we switch from () to another symbol, we would just change it in one place. Or we can modify to look for () first and if not found look for another symbol.

Here is the usage:

List<CustomJpg> jpgs = new List<CustomJpg>
{
    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();

You can use this approach for any object.




回答2:


In the above example code return a list of numbers ordered by numbers but if you want have list of file names that ordered by name better you put in same zero to beginning of the numbers like "some-name-(001).jpg" and you can simply order that

List<string> strings = new List<string>
        {
            "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);



回答3:


You can make the Substring selection easier, if you first cut off the part starting at the closing parenthesis ")". I.e., from "some-name-(5.1).jpg" you first get "some-name-(5.1". Then take the part after "(". This saves a length calculation, as the 2nd Substring automatically takes everything up to the end of string.

strings = strings
    .OrderBy(x => Decimal.Parse(
                      x.Substring(0, x.IndexOf(")"))
                       .Substring(x.IndexOf("(") + 1)
                  )
     )
    .ToList();

This is probably not of great importance here, but generally, decimal stores numbers given in decimal notation more accurately than double. double can convert 17.2 as 17.19999999999999.



来源:https://stackoverflow.com/questions/41625535/how-to-order-list-of-strings-on-number-substring-using-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!