【问题标题】:C# Linq Custom SortC# Linq 自定义排序
【发布时间】:2012-08-17 06:42:04
【问题描述】:

我在 linqtosql 中有一个返回 LabelNumber 的查询:

var q = from list in db.Lists
        select list.LabelNumber;

var q 然后变成IEnumerable<string> ,其元素如下:

{"1","2","2.A","2.B","3","3.A","3.B"}

我基本上想对上面显示的元素进行排序,但我不能使用OrderBy(x=>x.LabelNumber),因为"10" 会放在"1" 之后和"2" 之前。

我假设我必须编写一个自定义比较器函数,但是我该如何使用 linq 来实现呢?

编辑:我认为以下所有答案都有效,但必须在所有回复中添加一个警告。

如果您使用的是 Linq2SQL,则不能在查询中使用数组索引。为了克服这个问题,您应该有两个查询。从 SQL 读取的一种。第二个进行排序:

var q = from list in db.Lists
            select list.LabelNumber;

var q2 = q.AsEnumerable()
          .OrderBy(x => int.Parse(x.LabelNumber.Split('.')[0]))
          .ThenBy(x => x.Number
                        .Contains(".") ? 
                              x.LabelNumber.Split('.')[1].ToString() 
                              : 
                              string.Empty);

【问题讨论】:

标签: c# linq


【解决方案1】:
OrderBy(x=>x.LabelNumber, new AlphanumComparator())

AlphanumComparator 是 David Koelle 的出色 Alphanum natural sort algorithm。无需重新发明轮子。

如果您要使用 C# 版本,请将其更改为:

AlphanumComparator : IComparer<string>

public int Compare(string x, string y)

【讨论】:

  • 这种语法不起作用。 Argument 3: cannot convert from 'AlphanumComparator' to 'System.Collections.Generic.IComparer&lt;string&gt;'
  • 那是因为 OrderBy 需要一个通用的 IComparer,而 AlphanumComparator 是非通用的。不过,这种改变很容易做到。在声明中添加 ,将所有对象更改为 Ts 并删除强制转换。
  • 糟糕,我忘记了我修改了 AlphanumComparator 的本地副本。我刚刚提交了一个补丁给作者,我做了一些简单的修改。或许IComparer的支持会在未来的正式版中加入。
【解决方案2】:

您可能不必编写自定义比较器。如果你所有的标签都是number.letter的形式,你可以使用这个。

var query = from list in db.Lists
            let split = list.LabelNumber.Split('.')
            let order = split.Length == 1
                ? new { a = int.Parse(split[0]), b = String.Empty }
                : new { a = int.Parse(split[0]), b = split[1] }
            orderby order.a, order.b
            select list.LabelNumber;

如果您需要更多控制,您始终可以将 sortby 字段(ab)转换为适当的类型,而不是整数和字符串。


如果这是 LINQ-to-SQL,这实际上是行不通的,因为这里使用的某些方法不受支持。这是一个 LINQ-to-SQL 友好的版本。它不会产生最漂亮的查询,但它会起作用。

var query = from list in db.Lists
            let dot = list.LabelNumber.IndexOf('.')
            let name = list.LabelNumber
            let order = dot == -1
                ? new { a = Convert.ToInt32(name.Substring(0, dot)), b = String.Empty }
                : new { a = Convert.ToInt32(name.Substring(0, dot)), b = name.Substring(dot+1) }
            orderby order.a, order.b
            select list.LabelNumber;

【讨论】:

  • 当我运行这个时出现这个错误:System.InvalidOperationException: Unrecognized expression node: ArrayIndex
  • 这是在 LINQ-to-SQL 中对吗?当然,这在那儿是行不通的,需要进行一些调整。
  • 感谢您的编辑。您说它不会产生最漂亮的查询,因为它可能必须使用 TSQL 来处理子字符串;这个对吗?您的第二个版本如何将性能与使用 GetEnumerator() 方法进行比较。我假设您的方法需要数据库服务器进行更多工作,但我的方法使用更多内存。这些假设是否准确。您能讨论一下这些方法的性能吗?我只是在学习 Linq,并试图了解可以做什么和应该做什么。
  • C# 等效项更简洁,而 TSQL 则非常冗长。 AFAIK,Substring() 并不像使用IndexOf() 那样糟糕,就使查询变得复杂。您可能不必担心。你的假设是准确的。使用AsEnumerable(),您正在使用LINQ-to-Objects,并且在您的机器上执行以下查询(过滤、排序等)。如果你保持它对 LINQ-to-SQL 友好,那么一切都将在服务器上执行。毕竟,它只是一个 TSQL 查询。对于大型数据集,尽量保持 L2SQL 友好以减轻处理负担。否则,由您决定。
【解决方案3】:

如果您确定q 的格式和排序正确:

var result = q.OrderBy(x => int.Parse(x.Split('.')[0]));

【讨论】:

  • 你假设只有数字是乱序的。如果你有 2.B,2,2.A 怎么办?
  • @Michael:OP 并没有说他想按字母订购..我很懒惰。无论如何,他很可能想要它。哈哈。
【解决方案4】:

这是我的贡献.. 使用 Regular ExpressionLAMBDA expression

List<String> Lst = new List<string> { "1", "2", "2.A","10.A", "2.C", "3", "3.A", "3.B","2.B","11.D" };
Lst = Lst.Select(X => new
        {
            Number = int.Parse( Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[1].Value),
            Strings=Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[2].Value,
            OriginalString = X
        }).OrderBy(X => X.Number).ThenBy(X=>X.Strings)
        .Select(X => X.OriginalString).ToList();

输出:

"1"
"2"
"2.A"
"2.B"
"2.C"
"3"
"3.A"
"3.B"
"10.A"
"11.D"

【讨论】:

  • Method 'System.Text.RegularExpressions.Match Match(System.String, System.String)' has no supported translation to SQL. 虽然它在您的示例中似乎确实有效。
  • 然后使用您的标签编号创建list,然后使用上述表达式对其进行排序。
  • 解决了。谢谢,我让你用 Linq2SQL 运行代码。我不知道阵列限制。我最终使用了一个更简单的版本,因为正则表达式过度使我的情况复杂化。子嵌套最终会变得更加复杂,我将不得不求助于您的解决方案。谢谢。
猜你喜欢
  • 2019-06-22
  • 2014-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
  • 1970-01-01
相关资源
最近更新 更多