NSFetchedResultsController 中的自定义排序

发布于 2024-12-10 16:45:53 字数 1182 浏览 0 评论 0原文

花了几个小时后,我发现不可能在下面的文章中由 SQLite 支持的 NSFetchedResultsController 中进行自定义排序。

NSFetchedResultsController 自定义排序未被调用

但是,我找不到实际的解决方案我的问题。

这就是我正在尝试做的事情。

背景:
我在 CoreData 中有一个英语词典数据库(只是一个简单的单词列表 - 非常大)。使用 NSFetchedResultsController 在 UITableView 中显示单词。

UITableView 有一个关联的搜索栏。当用户在搜索栏中输入字符串时,UITableView 会显示过滤后的单词列表。

为什么我需要自定义排序:
当用户输入一个字符串时,假设它是 bre,我将其更改为正则表达式 b.*r.*e.* 并将其用作 NSPredicate,然后,执行performFetch。这样所有像“bare”和“break”这样的词都会显示在表格视图中。

默认情况下,单词按字母顺序显示。因此,bare 将出现在 break 之前。

我希望 break 位于搜索列表中的 bare 之前,因为 break 的前三个字符与用户输入的内容完全匹配。

可能的想法:

  1. 将 NSFetchedResultsController 的结果复制到 NSArray 中并进行自定义排序。 我不确定 NSArray 对于像英语字典这样的大型数组来说有多快。
  2. 多次尝试执行Fetch。例如上面的例子,尝试按顺序执行bre.*br.+e.*b.+r.+e.*的performFetch并将它们结合起来。

这两种想法看起来都不太简洁。

如果您能提出任何已知的整洁和建议,我将不胜感激。此类问题的典型解决方案。

After spending hours, I found that it was not possible to do customized sort in NSFetchedResultsController backed by SQLite from following article.

NSFetchedResultsController custom sort not getting called

However, I couldn't find actual solution for my problem.

Here's what I am trying to do.

Background:
I have a English dictionary database (just a simple list of words - very large) in CoreData. Words are shown in UITableView using NSFetchedResultsController.

UITableView has an associated Search Bar. When an user inputs a string in Search Bar, UITableView shows a list of words filtered.

Why do I need customized sort:
When a user inputs a string, let's say it was bre, I change it to regular expression, b.*r.*e.* and use it as a NSPredicate, then, do performFetch. So that all words like 'bare' and 'break' will be shown in the table view.

By default, the words are shown as a alphabetical order. Therefore, bare will come before break.

I want break to come before bare on the searched list because the first three character of break exactly matches to what the user input.

Possible ideas:

  1. Copy the result of NSFetchedResultsController into NSArray and do the custom sort.
    I am not sure how fast NSArray will work for a large array like an English dictionary.
  2. Try performFetch multiple times. For example above, try performFetch for bre.*, br.+e.*, b.+r.+e.* in order and combine them.

Both ideas don't look very neat.

I appreciate if you can suggest any known neat & typical solution for this kind of problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

橘寄 2024-12-17 16:45:53

您可以考虑的另一种方法是使用瞬态属性来装饰结果以指示匹配的类型,然后使用该瞬态属性作为第一个排序描述符。

这需要循环遍历所有结果,再次比较字符串,并设置属性。如果您期望得到一长组结果,使用数组可能同样简单。

如果您需要有效地处理大型结果集,我建议使用两种排序描述符,一种仅返回精确匹配,另一种仅返回非精确匹配。然后显示第一个结果,然后显示第二个结果。使用应该可以完成的复合谓词。

Another approach you could consider is using a transient property to decorate the results to indicate the type of match, and then using that transient property as the first sort descriptor.

This requires looping through all the results, comparing the strings again, and setting the attributes. If you expect a long set of results, using an array might be just as easy.

If you need to handle large result sets efficiently, I'd suggest using two sort descriptors, one which returns only exact matches, and another which returns only non-exact matches. Then display the results from the first followed by the results from the second. With a compound predicate that should be possible to accomplish.

桃扇骨 2024-12-17 16:45:53

哇,这个问题很烦人。

我的设置如下。我有一个搜索,它接受输入并通过匹配用户名或全名来查找用户。服务器已经返回了适当的顺序,但由于我使用的是 NSFetchedResultsController,我需要一些排序描述符,这就是我所做的,似乎运行良好。我向我的用户实体添加了一个名为 matchScore 的新属性,在从服务器进行 CRUD 期间,我获得了查询 <-> 之间的 MIN() Levenshtein Distance 分数。用户名和查询<->全名

我现在有一个排序描述符,它将按服务器中与用户查询最接近的匹配结果进行排序。代码是 ruby​​motion,但应该仍然可读。

sortDescriptors = []
sortDescriptors << NSSortDescriptor.sortDescriptorWithKey("matchScore", ascending:true)

使用新的排序描述符,我现在可以获取“不太理想”的结果,并且仍然首先保留最接近的匹配。我现在可以避免 @Jaemin 的一些潜在解决方案,这些解决方案涉及复杂的结果聚合,以避免自定义排序不起作用。

request.predicate = NSPredicate.predicateWithFormat("(username MATCHES[cd] %@) OR (username BEGINSWITH[cd] %@) OR (name CONTAINS[cd] %@)", argumentArray:[searchString, searchString, searchString])

现在,比赛分数是通过服务器的 CRUD 生成的。

usersContext.performBlock(lambda{
  restUsers.each do |restUser|
    user = User.entityWithRestModel(restUser, usersContext)
    user.matchScore = [query.compareWithWord(user.username, matchGain:10, missingCost:1), query.compareWithWord(user.name, matchGain:10, missingCost:1].min
    puts "u:#{user.username} <-> q:#{query}   score:#{user.matchScore}"
  end
})

这是我用来获取 Levenshtein 距离的 NSString 类别。 https://gist.github.com/iloveitaly/1515464

Wow, this problem was annoying.

My setup is as follows. I have a search that takes input and looks for users by matching username or full name. The server was already returning the appropriate order, but since i'm using NSFetchedResultsController I need some sort descriptor Here is what I did that seems to be working well. I added a new property to my user entity called matchScore and during CRUD from the server I get the MIN() Levenshtein Distance score between the query <-> username and query <-> full name

I now have a sort descriptor that will order by the closest matching results from the server with the user's query. The code is rubymotion, but should still be readable.

sortDescriptors = []
sortDescriptors << NSSortDescriptor.sortDescriptorWithKey("matchScore", ascending:true)

With the new sort descriptor I can now fetch "less than ideal" results and still keep closest matches first. I can now avoid some of @Jaemin's potential solutions which involved complicated result aggregation to get around custom sorts not working.

request.predicate = NSPredicate.predicateWithFormat("(username MATCHES[cd] %@) OR (username BEGINSWITH[cd] %@) OR (name CONTAINS[cd] %@)", argumentArray:[searchString, searchString, searchString])

The match score is now generated on CRUD from the server.

usersContext.performBlock(lambda{
  restUsers.each do |restUser|
    user = User.entityWithRestModel(restUser, usersContext)
    user.matchScore = [query.compareWithWord(user.username, matchGain:10, missingCost:1), query.compareWithWord(user.name, matchGain:10, missingCost:1].min
    puts "u:#{user.username} <-> q:#{query}   score:#{user.matchScore}"
  end
})

Here is the NSString category that I am using to get the Levenshtein distance. https://gist.github.com/iloveitaly/1515464

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文