简体   繁体   中英

How can i sort a List<string> according to the numbers in each string in each index?

In the end in threadList for example in index 0 i have:

1. hello world

Then in index 2 i have:

33. hello

Then in index 3 i have:

76. hi

In index 4:

2. good

The numbers 1 33 76 2 are part of the string in each index.

1. hello world is a string. I want to sort the List according to the numbers so in this example it should be:

1. hello world
2. good
33. hello
76. hi

And again the numbers are part of the string they are not int.

This is the method that create the List:

public string GetResponsers(string contents)
        {
            string responser = "";
            List<string> threadList = new List<string>();
            int f = 0;
            int startPos = 0;
            while (true)
            {
                string firstTag = "<FONT CLASS='text16b'>";
                string lastTag = "&n";
                f = contents.IndexOf(firstTag, startPos);
                if (f == -1)
                {
                    break;
                }
                int g = contents.IndexOf(lastTag, f);
                startPos = g + lastTag.Length;
                responser = contents.Substring(f + 22, g - f - 22);

                threadList.Add(responser);
            }
            return responser;
        }

I think I see what you want...

yourList.OrderBy(str => {
    var match = Regex.Match(str, @"^([-+]?\d+)");
    return match.Success ? int.Parse(match.Groups[1].Value) : int.MaxValue;
});

This will return an IEnumerable<string> ordered by the value of the integer number that appears at the beginning of the string. Entries without a number will be put at the end.


EDIT: If you want the variable yourList to be ordered, then you have to reassign it:

yourList = yourList
    .OrderBy(str => {
        var match = Regex.Match(str, @"^([-+]?\d+)");
        return match.Success ? int.Parse(match.Groups[1].Value) : int.MaxValue;
    })
    .ToList();

If and i say again If the the examples in the list i will present to you are in the same format you have then you simply apply sort method which will sort automaticaly:

            List<string> lst = new List<string>()
            {
                "1. hello world",
                "76. hi",
                "33. hello",
                "2. good"
            };

            lst.Sort();//this will sort the list

After this the list will have the output you want.

EDIT: As was pointed out(and very right indeed) i forgot about those cases where there was a 19 for example in this case apply the sort with a comparison:

     lst.Sort((s1, s2) =>
     {
          string first = new string(s1.TakeWhile(c => char.IsDigit(c)).ToArray());
          string second = new string(s2.TakeWhile(c => char.IsDigit(c)).ToArray());
          return int.Parse(first).CompareTo(int.Parse(second));
     });

I think you could also try something like this:

yourList.OrderBy(s => {
    int num;
    var parts = string.Split(s, '.');
    if (parts.Count > 0 && int.TryParse(parts[0], out num))
        return num;

    return int.MaxValue;
});

In this way, you'll ensure a numeric sort of the numeric portion of the string without using a regular expression.

If you're on Windows 7 or greater, it has a CompareStringEx function you could P/Invoke into. Its SORT_DIGITSASNUMBERS option does exactly what you want:

    static int CompareCSEx(string x, string y)
    {
        return CompareStringEx("", SORT_DIGITSASNUMBERS, x, x.Length,
                               y, y.Length, null, null, IntPtr.Zero) - 2;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern int CompareStringEx(String strLocale, uint flags,
                                      String str1, int count1, string str2,
                                      int count2, string version,
                                      string reserved, IntPtr param);

    const int SORT_DIGITSASNUMBERS = 8;

Note this will treat all digits in the string as numbers, not just the digits at the start of the string.

And now dumb benchmark showing it running nearly 4x faster than fourpastmidnight's solution:

static void Main(string[] args)
{
    Random r = new Random(0);

    string[] values = (from i in Enumerable.Range(1, 500)
                        let order = r.Next()
                        orderby order
                        select i.ToString(CultureInfo.InvariantCulture)
                                    + ". " + RandomString(r)).ToArray();

    string[] fpmValues = values.ToArray();
    string[] csexValues = values.ToArray();

    Benchmark("FPM ", () => Array.Sort(fpmValues,
        new Comparison<string>(CompareFpm)));

    Benchmark("CSEX", () => Array.Sort(csexValues,
        new Comparison<string>(CompareCSEx)));

    Console.WriteLine("Sort equal: {0}",
        Enumerable.SequenceEqual(fpmValues, csexValues));
}

static string RandomString(Random r)
{
    int len = r.Next(1, 32);
    char[] buf = new char[len];

    for (int i = 0; i < len; ++i)
    {
        buf[i] = (char)r.Next('A', 'Z');
    }

    return new string(buf);
}

static int CompareFpm(string x, string y)
{
    int xi, yi;

    var parts = x.Split('.');
    if(parts.Length == 0 || !int.TryParse(parts[0], out xi))
    {
        xi = int.MaxValue;
    }

    parts = y.Split('.');
    if (parts.Length == 0 || !int.TryParse(parts[0], out yi))
    {
        yi = int.MaxValue;
    }

    return xi - yi;
}

static void Benchmark(string name, Action a)
{
    long start = Stopwatch.GetTimestamp(), end;
    long runs = 0;

    do
    {
        a();
        ++runs;
        end = Stopwatch.GetTimestamp();
    }
    while ((end - start) < (Stopwatch.Frequency * 5));

    Console.WriteLine("{0}: {1} runs/sec", name, runs / 5);
}

With output:

FPM : 681 runs/sec
CSEX: 2577 runs/sec
Sort equal: True

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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