【问题标题】:Sort field that contains numbers and letters包含数字和字母的排序字段
【发布时间】:2016-08-18 18:30:59
【问题描述】:

我有一个numbersnumber-letterletter-number 的列表。我的列的类型是字符串。我的数据是这样的:

1
14
3
S-34
2
36-1/E
26
S-14
20
S-2
19
36-1
30
35
S-1
34

但我想这样排序:

1
2
3
14
20
25
30
35
36-1
36-1/E
S-1
S-2
S-14
S-34

但我的代码是这样对数据进行排序的:

1
14
19
2
20
25
3
30
35
36-1
36-1/E
S-1
S-14
S-2
S-34

我的代码是:

List<ViewTestPackageHistorySheet> lstTestPackageHistorySheets = _reportTestPackageHistorySheetRepository.ShowReport(Id).OrderBy(i => i.JointNumber).ToList();

我更改了下面的代码但失败了。

List<ViewTestPackageHistorySheet> lstTestPackageHistorySheets = _reportTestPackageHistorySheetRepository.ShowReport(Id).OrderBy(i => Convert.ToInt32(i.JointNumber)).ToList();

错误是:

LINQ to Entities does not recognize the method 'Int32 ToInt32(System.String)' method, and this method cannot be translated into a store expression.

【问题讨论】:

标签: c# linq sorting


【解决方案1】:

使用以下 Alphanumericsorter,它在内部使用 PInvoke

public class AlphaNumericSorter : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return SafeNativeMethods.StrCmpLogicalW(x, y);
    }
}

[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
    public static extern int StrCmpLogicalW(string psz1, string psz2);
}

用法:

List<string> testList = // Your input list;

testList.Sort(new AlphaNumericSorter());

现在testList 包含问题中预期的排序数据

【讨论】:

    【解决方案2】:

    创建一个自定义Comparer&lt;string&gt; 并实现您想要的逻辑(当然对于大型列表来说效率低下,您应该对其进行调整):

     var data = list.OrderBy(x => x, new SpecialComparer());
    

    特殊比较器

       public class SpecialComparer : IComparer<string>
            {
                public int Compare(string x, string y)
                {
                    if (x.Equals(y))
                        return 0;
    
                    int yn;
                    int xn;
                    bool isXNumber = int.TryParse(x, out xn);
                    bool isYNumber = int.TryParse(y, out yn);
    
                    if (isXNumber && isYNumber)
                    {
                        return xn.CompareTo(yn);
                    }
                    else if (isXNumber && !isYNumber)
                    {
                        return -1;
                    }
                    else if (!isXNumber && isYNumber)
                    {
                        return 1;
                    }
                    else if(x.Any(c => c == '/' || c == '-' ) || y.Any(c => c == '/' || c == '-'))
                    {
                        var xParts = x.Split("/-".ToCharArray(),  StringSplitOptions.RemoveEmptyEntries);
                        var yParts = y.Split("/-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    
                        var minLen = Math.Min(xParts.Length, yParts.Length);
    
                        var result = 0;
                        for (int i = 0; i < minLen; i++)
                            if ((result = Compare(xParts[i], yParts[i])) != 0)
                                return result;
    
                        return x.Length < y.Length ? -1 : 1;
                    }
                    else
                    {
                        return x.CompareTo(y);
                    }
                }
            }
    

    【讨论】:

    • 我收到此错误:严重性代码描述项目文件行错误 CS0411 方法 'Enumerable.OrderBy(IEnumerable, Func, IComparer)' 不能从用法中推断出来。尝试明确指定类型参数
    • 这是我的代码:_reportTestPackageHistorySheetRepository.ShowReport(Id).ToList().OrderBy(x=>x, new SpecialComparer());
    • 获取这个列表-"1017", "650", "650C", "650B", "W323", "10", "20", "1000", "W1000", "W900" ,上面发布的解决方案不适用于以字母开头的元素,它会导致W323, W900, W1000,但解决方案结果是W1000, W323, W900,因为我们不是比较每个字符
    • @MrinalKamboj,我只对问题描述进行了解决。没有包含一个字母后跟没有空格字符的数字的数据项
    【解决方案3】:

    Linq 到 Entities 不支持 Convert.ToInt32 这就是导致您的情况出现错误的原因,我建议具体化查询,然后应用您的订购。

    List<ViewTestPackageHistorySheet> lstTestPackageHistorySheets = _reportTestPackageHistorySheetRepository.ShowReport(Id).ToList();
    
    // Now order the list. 
    lstTestPackageHistorySheets = lstTestPackageHistorySheets.Select(x=> 
                 new 
                 {   // split number and string
                     number =  Regex.IsMatch(x.Split('-')[0],@"^\d+$")? int.Parse(x.Split('-')[0]): int.MaxValue, 
                     item =x
                 })
        .OrderBy(x=>x.number)    // first order by number
        .ThenBy(x=>x.item)       // then order by string. 
        .Select(x=>x.item)
        .ToList();
    

    检查这个Demo

    【讨论】:

    • 获取这个列表 - "1017", "650", "650C", "650B", "W323", "10", "20", "1000", "W1000", "W900" ,上面发布的解决方案不适用于以 Alphabet 开头的元素,它会导致 W323, W900, W1000,但解决方案结果是 W1000, W323, W900,因为我们不是比较每个字符
    • 啊...您应该在问题中解释一下,根据您的示例,我认为如果它以数字开头,则有一个分隔符,但请记住想法是相同的。如果数字以数字开头,则只需提取数字即可。
    • 我不是 OP :),尽管如此,你可以更正答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    • 2019-03-28
    相关资源
    最近更新 更多