IOrderComparer独特的订购情况

时间:2015-08-11 19:28:29

标签: c# linq

我试图以独特的方式订购混合字符串,并且想知道其他人是否已经这样做了。我找到了几篇关于使用IOrderConparer的文章,但找不到我特定排序问题的解决方案。

我有以下内容:

1017,650,650C,650B,W323,10,20,1000,W1000

我需要按如下方式订购:

10,20,650,650B,650C,1000,1017,W323,W1000

任何帮助将不胜感激。感谢。

2 个答案:

答案 0 :(得分:1)

使用StrCmpLogicalW实现比较:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string x, string y);

这将是一个逻辑(将数字考虑在内)字符串比较而不是基于标准字符串的字符串,它将为您提供您所追求的结果。

答案 1 :(得分:1)

稍长但所有托管代码

public class LogicalSorter : IComparer
{
    public int Compare(object a, object b)
    {
        var first = Regex.Split((string)a,"([0-9]+)").Where(s => s != "").ToArray();
        var second = Regex.Split((string)b,"([0-9]+)").Where(s => s != "").ToArray();

        var endIdx = Math.Min(first.Count(), second.Count());

        for (var i = 0; i < endIdx; i++)
        {
            var part1 = first.ElementAt(i);
            var part2 = second.ElementAt(i);

            if (part1.All(char.IsDigit) && part2.All(char.IsDigit) && part1 != part2)
            {
                return int.Parse(part1).CompareTo(int.Parse(part2));
            }

            if (part1 != part2) return part1.CompareTo(part2);
        }

        return first.Count().CompareTo(second.Count());
    }
}

像这样使用

string[] values = { "1017", "650", "650C", "650B", "W323", "10", "20", "1000", "W1000" };

Array.Sort(values, new LogicalSorter());

foreach (var value in values)
   Console.WriteLine(value);

或使用Mrinal建议的泛型(首选)

public class LogicalSorter : IComparer<String>
{
    public int Compare(String a, String b)
    {
        var first = Regex.Split(a, "([0-9]+)").Where(s => s != "").ToArray();
        var second = Regex.Split(b, "([0-9]+)").Where(s => s != "").ToArray();

        var endIdx = Math.Min(first.Count(), second.Count());

        for (var i = 0; i < endIdx; i++)
        {
            var part1 = first.ElementAt(i);
            var part2 = second.ElementAt(i);

            if (part1.All(char.IsDigit) && part2.All(char.IsDigit) && part1 != part2)
            {
                return int.Parse(part1).CompareTo(int.Parse(part2));
            }

            if (part1 != part2) return part1.CompareTo(part2);
        }

        return first.Count().CompareTo(second.Count());
    }
}

优化托管代码示例(速度不适用于外观),执行 47x 正则表达式版本

public class LogicalSorter : IComparer<String>
{
    public int Compare(String a, String b)
    {
        var aLength = a.Length;
        var bLength = b.Length;
        var aIdx = 0;
        var bIdx = 0;
        int aPartLen;
        int bPartLen;
        int aPartEndIndex;
        int bPartEndIndex;
        bool aIsString;
        bool bIsString;

        // Examine both strings on character level, keep track of where
        // we are in each string since lengths might differ
        while (aIdx < aLength && bIdx < bLength)
        {
            // If both strings contain digit at current index
            // compare numbers
            if (char.IsDigit(a[aIdx]) && char.IsDigit(b[bIdx]))
            {
                // Get longest consecutive list of digits from each string
                aPartEndIndex = aIdx;
                while (aPartEndIndex < aLength && char.IsDigit(a[aPartEndIndex])) { aPartEndIndex++; }

                bPartEndIndex = bIdx;
                while (bPartEndIndex < bLength && char.IsDigit(b[bPartEndIndex])) { bPartEndIndex++; }

                aPartLen = aPartEndIndex - aIdx;
                bPartLen = bPartEndIndex - bIdx;

                // Compare lengths (longest number is greater)
                if (aPartLen != bPartLen) return aPartLen < bPartLen ? -1 : 1;

                // Same length numbers, compare chars until not same or end
                while (aIdx < aPartEndIndex && a[aIdx] == b[bIdx])
                {
                    aIdx++;
                    bIdx++;
                }

                // If not at end compare last characters that were not same
                if(aIdx != aPartEndIndex)
                    return a[aIdx] < b[bIdx] ? -1 : 1;
            }
            else
            {
                // Comparing string vs number or string vs string
                aIsString = char.IsLetter(a[aIdx]);
                bIsString = char.IsLetter(b[bIdx]);

                // if not 2 strings, number is always first
                if (aIsString != bIsString) return aIsString ? 1 : -1;

                // Get longest consecutive list of letters from each string
                aPartEndIndex = aIdx;
                while (aPartEndIndex < aLength && (char.IsLetter(a[aPartEndIndex]) == aIsString))
                {
                    aPartEndIndex++;
                }

                bPartEndIndex = bIdx;
                while (bPartEndIndex < bLength && (char.IsLetter(b[bPartEndIndex]) == bIsString))
                {
                    bPartEndIndex++;
                }

                // Compare chars until not same or end
                while (aIdx < aPartEndIndex && bIdx < bPartEndIndex && a[aIdx] == b[bIdx])
                {
                    aIdx++;
                    bIdx++;
                }

                // if not at end compare last letters found
                if ((aIdx != aPartEndIndex) || (bIdx != bPartEndIndex))
                    return a[aIdx] < b[bIdx] ? -1 : 1;
            }
        }

        // Use length as tie breaker
        return aLength < bLength ? -1 : 1;
    }
}