NSFetchedResultsController中的自定义排序

时间:2011-10-16 02:45:11

标签: iphone sorting nsfetchedresultscontroller nssortdescriptor

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

NSFetchedResultsController custom sort not getting called

但是,我无法找到解决问题的实际方法。

这就是我想要做的事情。

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

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

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

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

我希望breakbare之前到达搜索列表,因为break的前三个字符与用户输入完全匹配。

可能的想法:

  1. 将NSFetchedResultsController的结果复制到NSArray中并执行自定义排序。 我不确定NSArray对于像英语词典这样的大型数组有多快。
  2. 多次尝试performFetch。例如,请按顺序尝试执行bre.*br.+e.*b.+r.+e.*的performFetch并合并它们。
  3. 这两种想法看起来都不太整洁。

    如果你能提出任何已知的整洁和优秀的话,我感激不尽。解决这类问题的典型方法。

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

哇,这个问题很烦人。

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

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

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