【问题标题】:Order for autocomplete自动完成订单
【发布时间】:2012-08-19 22:39:53
【问题描述】:

假设我们有一个Humans 的集合:

public class Human
{
    public string FirstName { get; set; }
    public string SecondName { get; set; }
    public string CompanyName { get; set; }
}

var people = new List<Human>(){...};

我们如何在先按FirstName 然后按SecondName 再按CompanyName 对人员进行排序时实现自动完成?

我试过了:

people.Where(x => x.FirstName.StartsWith(term) || x.SecondName.StartsWith(term) 
  ||  x.CompanyName.StartsWith(term))
  .OrderBy(x => x.FirstName).ThenBy(x => x.SecondName).ThenBy(x => x.CompanyName)

但这不能正常工作。我想首先查看所有匹配的 FirstName 字段,然后只查看 SecondName 字段等等。

【问题讨论】:

  • 尽管有所有这些赞成,但没有一个解决方案实际上会改变自动完成下拉菜单的顺序。 See here

标签: c# linq autocomplete


【解决方案1】:
people.Where(x => x.FirstName.StartsWith(term)).OrderBy(x => x.FirstName)
.Concat(people.Where(x => x.SecondName.StartsWith(term)).OrderBy(x => x.SecondName))
.Concat(people.Where(x => x.CompanyName.StartsWith(term)).OrderBy(x => x.CompanyName))

是否将.Distinct() 添加到末尾取决于您是否要排除一个人以多种方式匹配的情况(在这种情况下,您也可以使用.Union() 而不是.Concat(),具体取决于您的来源- 它被记录为保留可枚举来源的顺序,但不包含其他可查询的来源,因此它可能会根据people 的来源混淆排序。

【讨论】:

  • 我看到有人给出了相同的答案并删除了它。我认为他们是正确的,除了将其描述为“三个不同的查询”,这是他们的答案中的一个查询,虽然它有其他查询作为创建该查询的步骤,但实际上只执行了一个查询。
  • 老实说,我更喜欢这个回答。
【解决方案2】:

我猜你会想要的

people.Where(x => x.FirstName.StartsWith(term) ||
                  x.SecondName.StartsWith(term) ||
                  x.CompanyName.StartsWith(term))
    .OrderByDescending(x => x.FirstName.StartsWith(term))
    .ThenByDescending(x => x.SecondName.StartsWith(term))
    .ThenBy(x => x.FirstName)
    .ThenBy(x => x.SecondName)
    .ThenBy(x => x.CompanyName)

这样放

  • 名字最先匹配的每个人
  • 那么第二个名字匹配的每个人
  • (那么,每个人的公司名称都会匹配)
  • 在此订单中,它会像以前一样按名字/姓氏/公司名称排序。

是/否排序需要Descending,因为false 小于true

您可以通过 Select 使用人员和 FirstNameMatchesSecondNameMatches 等三个布尔值来稍微优化这一点,然后在 Where 和排序子句中使用这些布尔值,从而避免调用StartsWith 5 次而不是 3 次,但是当您进行基本排序时,您可以担心这一点。

如果您想按第二名对第二名匹配排序,而按公司名称对公司名匹配进行排序,那就比较麻烦了。

【讨论】:

  • 是的,我只是选择“在现有查询中添加一些行”而不是“正确重写”;)
  • 虽然我不认为它的效率低到值得一票否决:-/
  • 当然不是。对于数据库源,我希望它的性能与任何其他源几乎相同。在记忆中,很难预测哪个会更好。如果我真的想为性能付出很多努力,我会采用你的(单个过滤器,与我的不同)并将排序转换为针对自定义比较器的单个排序。
  • 再一次,如果我真的想要很多性能,我可能会预加载前缀查找并完全跳过任何基于 linq 的解决方案(可能除了在查询中填充它)。鉴于用例是自动完成,这实际上可能是要走的路,因为自动完成会导致轻微的延迟变得非常明显。
  • @Jon 谢谢你。选择性地不使用 LINQ 可能是最好的方法。
【解决方案3】:

您正在做的错误是在OrderBy() 之后使用ThenBy()。使用它,您将在内部重新排序已排序的列表,而不是按属性是否匹配进行排序。

你应该做的是用三种方式过滤列表,然后加入它们并使用Union()删除重复项。

那又怎样:

people.Where( x => x.FirstName.StartsWith( term ) ).OrderBy( x => x.FirstName )
      .Union( people.Where( x => x.SecondName.StartsWith( term ) ).OrderBy( x => x.SecondName ) )
      .Union( people.Where( x => x.CompanyName.StartsWith( term ) ).OrderBy( x => x.CompanyName ) );

编辑:

正如 Jon Hanna 所指出的,如果您使用 Queryable.Union(),这将无法正常工作,因为它不会保留查询的顺序:

Enumerable.Union() 被指定为保留顺序但 Queryable.Union() 不是。给定的实现可以,但不能 必须和另一个(或对同一个更新)可能不会。代码 因此,如果人们给定,则可以以任何顺序给出结果 不是内存中的源。乔恩·汉娜

【讨论】:

  • 这正是ThenBy 的用途——在一级排序之后添加二级排序。 错误是按名称而不是名称是否匹配。
  • +1 是的,我知道。我的意思是在这种情况下他不应该使用ThenBy()。我将编辑我的答案,感谢您的澄清。
  • Enumerable.Union() 被指定为保留顺序,但Queryable.Union() 不是。一个给定的实现可以,但它不是必须的,而另一个(或对同一实现的更新)可能不会。因此,如果people 不是内存中的源,那么给出的代码可以以任何顺序给出结果。
  • 感谢您的添加。我从来没有在这个网站上问过问题,但我已经在这里回答了很多问题,因为我一直是那个人,“啊,这和我的问题一模一样”:)
  • 是的,你完全正确。它也发生在我身上。事实上,有时澄​​清和规范非常有帮助。
猜你喜欢
  • 2016-04-17
  • 2020-01-17
  • 2016-06-11
  • 2021-12-27
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
相关资源
最近更新 更多