繁体   English   中英

对数字进行排序,然后对字符串进行排序 C#

[英]Sort numbers then strings C#

我想要这个列表

A
B
C
111
11
123
1
42
5

待排序

1
5
11
42
111
123
A
B
C

默认情况下,它像字符串一样对数字进行排序(因此,它是 1,11,111,123,42,5),但我想像数字一样对数字进行排序,而不是不是数字的字符串。

是否有干净的解决方案可以像上面那样对其进行排序?

它是一个对象列表,object 有几个属性,其中一个是 this 字符串。

这对于大多数用例都适用,但是如果字符串以控制字符开头,则诸如“ \\ tabc”之类的字符串将在整数之前出现:

list.OrderBy(x=>int.TryParse(x, out var dummy) ? dummy.ToString("D10") : x);

或7之前的C#版本:

list.OrderBy(x=> { int dummy; return int.TryParse(x, out dummy) ? dummy.ToString("D10") : x;} );

几个月前,我写了这个IComparer实现来处理这样的事情。 我认为它会默认执行您想要的操作,尽管它是为处理更复杂的情况而设计的,其中数字/字母组由分隔符分隔,分隔符也需要按原子顺序进行排序。 您应该能够根据需要进行调整。

public class SemanticComparer : IComparer<string>
{
    private static Regex _splitter = new Regex("\\W+");

    public int Compare(string x, string y)
    {
        string[] partsX = _splitter.Split(x);
        string[] partsY = _splitter.Split(y);

        int shortest = Math.Min(partsX.Length, partsY.Length);

        for (int index = 0; index < shortest; index++)
        {
            int intX, intY;
            int result;

            if (int.TryParse(partsX[index], out intX) && int.TryParse(partsY[index], out intY))
            {
                result = intX.CompareTo(intY);
            }
            else
            {
                result = string.Compare(partsX[index], partsY[index], StringComparison.Ordinal);
            }

            if (result != 0)
            {
                return result;
            }
        }

        return 0;
    }
}

您可以像这样对列表进行排序:

MyList.Sort(new SemanticComparer());

您想要的是自然排序

我曾经为此写过一些代码:

public static class NaturalCompare
{
    public static int Compare(string first, string second, StringComparison comparison = StringComparison.Ordinal)
    {
        if (string.Compare(first, second, comparison) == 0)
        {
            return 0;
        }

        if (first == null)
        {
            return -1;
        }

        if (second == null)
        {
            return 1;
        }

        DateTime d1, d2;

        if (DateTime.TryParse(first, out d1) && DateTime.TryParse(second, out d2))
        {
            return d1.CompareTo(d2);
        }

        var pos1 = 0;
        var pos2 = 0;

        int result;
        do
        {
            bool isNum1, isNum2;

            var part1 = GetNext(first, ref pos1, out isNum1);
            var part2 = GetNext(second, ref pos2, out isNum2);

            if (isNum1 && isNum2)
            {
                result = long.Parse(part1).CompareTo(long.Parse(part2));
            }
            else
            {
                result = String.Compare(part1, part2, comparison);
            }
        } while (result == 0 && pos1 < first.Length && pos2 < second.Length);

        return result;
    }

    public static int CompareToNatural(this string first, string second, StringComparison comparison = StringComparison.Ordinal)
    {
        return Compare(first, second, comparison);
    }

    public static IOrderedEnumerable<TSource> OrderByNatural<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
    {
        return source.OrderBy(keySelector, new NatComparer());
    }

    public static IOrderedEnumerable<TSource> OrderByNaturalDescending<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
    {
        return source.OrderByDescending(keySelector, new NatComparer());
    }

    private sealed class NatComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            return NaturalCompare.Compare(x, y);
        }
    }

    private static string GetNext(string s, ref int index, out bool isNumber)
    {
        if (index >= s.Length)
        {
            isNumber = false;
            return "";
        }

        isNumber = char.IsDigit(s[index]);

        var start = index;
        while (index < s.Length && char.IsDigit(s[index]) == isNumber)
        {
            index++;
        }
        return s.Substring(start, index - start);
    }
}

您可以遍历所有值一次,然后使用int.TryParse将它们分成两个单独的列表:一个用于int.TryParse返回true(又称数字)的值,另一个用于其中返回false的值的列表(非数字)。 然后,您可以分别对这两个列表进行排序,最后将它们的排序结果连接在一起。

我尚未测试此代码的性能,但是您可以使用比较器解决此问题

public class ArrayItemComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xInt = 0, yInt = 0;

        bool parseX = int.TryParse(x, out xInt);
        bool parseY = int.TryParse(y, out yInt);

        if (parseX && parseY)
        {
            return xInt.CompareTo(yInt);
        }
        else if (parseX)
        {
            return -1;
        }
        else if (parseY)
        {
            return 1;
        }
        else
        {
            return x.CompareTo(y);
        }
    }
}

我已经为此创建了一个解决方案。 我将list分为两部分,然后进行排序和合并。 请检查以下内容:

public List<ListItem> getSortedList()
{
    int dummy = 0;

    List<ListItem> list = new List<ListItem>();
    list.Add(new ListItem() { Item = "A" });
    list.Add(new ListItem() { Item = "B" });
    list.Add(new ListItem() { Item = "C" });
    list.Add(new ListItem() { Item = "111" });
    list.Add(new ListItem() { Item = "11" });
    list.Add(new ListItem() { Item = "123" });
    list.Add(new ListItem() { Item = "1" });
    list.Add(new ListItem() { Item = "42" });
    list.Add(new ListItem() { Item = "5" });

    var listNumber = list.Where(m => int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => Convert.ToInt16(m.Item)).ToList();
    var listString = list.Where(m => !int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => m.Item).ToList();

    var sortedList = listNumber.Concat(listString).ToList();

    return sortedList;
}

您可以在DotNetFiddle中运行它。

假设您从一组字符串开始,那么一个简单的比较器就可以完成此工作:

public class Comparer : IComparer<string>
{
    public int Compare(string a, string b) 
    {
        int ia = 0;
        int ib = 0;
        var aIsInt = int.TryParse(a,out ia);
        var bIsInt = int.TryParse(b,out ib);
        if (aIsInt == bIsInt)
        {
            if (aIsInt)
            {
                return ia.CompareTo(ib);
            }
            else
            {
                return a.CompareTo(b);
            }
        }
        return aIsInt ? -1 : 1;
    }
}

这是一个小提琴

使用“OrderBy”中的 Regex.Replace,这是一行(相当)简单的代码。 请注意,数字“3”必须等于或大于您最长的字符串,因此对于其他任何人,请根据需要增加。

using System.Text.RegularExpressions;

string[] yourStrings = new string[] { "A", "B", "C", "111", "11", "123", "1", "42", "5" };

foreach (var item in yourStrings.OrderBy(x => Regex.Replace(x, @"\d+", i => 
i.Value.PadLeft(3, '0'))))
{
    Response.Write(item + "\n");
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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