在 Swift 中将两个 NSPredicates 与独立的 NSSortDescriptors 结合起来

Posted

技术标签:

【中文标题】在 Swift 中将两个 NSPredicates 与独立的 NSSortDescriptors 结合起来【英文标题】:Combining two NSPredicates with independent NSSortDescriptors in Swift 【发布时间】:2016-12-28 07:34:27 【问题描述】:

我正在使用 CoreData 和 Swift 3,但我认为这更像是关于 NSPredicates、NSSortDescriptors 和 NSFetchedResultsController 的一般性问题...

我有一个要排序的具有整数属性的实体,但我需要根据传入的整数拆分结果,并按降序对两半进行独立排序,然后将它们组合起来。所有属性分割值的实体都在最后(降序)。

这是一个例子:

按整数属性降序排列的正常抓取结果:

[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

给定分割值“5”,最终结果应该是:

[5, 4, 3, 2, 1, 10, 9, 8, 7, 6]

或者拆分值为8,最终的结果应该是:

[8, 7, 6, 5, 4, 3, 2, 1, 10, 9]

我正在做的是创建一个翻转点,就像每月在 28、29、30 或 31 天翻转一样。在我的情况下,翻转点是动态的,我不使用天。

我无法弄清楚如何使用谓词和排序描述符来做到这一点。这甚至可能吗?

我已经阅读了关于在实体中使用“sortOrder”属性进行多次提取的信息。基本上,获取一次,将结果放入数组中,按照我想要的方式对数组进行排序,然后在每个实体上设置“sortOrder”属性,保存上下文,然后再次获取,按“sortOrder”排序。我可以做到,但我希望有更优雅的东西。

下面是一些对数组进行正确排序的代码:

func rolloverSort(withArray: [Int], andIndex index: Int) -> [Int] 
    let inputArray = withArray.sorted()
    var newArray = [Int]()
    var splitIndex: Int = 0

    for item in inputArray 
        if item <= index 
            newArray.insert(item, at: 0)
            splitIndex += 1
        
        if item > index 
            newArray.insert(item, at: splitIndex)
        
    
    return newArray

结果将驱动 UITableView,因此我需要在 NSFetchedResultContoller 的上下文中执行此操作,这就是我考虑谓词和排序描述符的原因。

这是我的 NSFetchedResultsController 和一些周围的上下文:

// sortMethod (used in sortDescriptor) is set via an action sheet button.
// Currently it's just a string for the entity attribute to sort on.
// The closure on each button sets self._fetchedRequestController = nil,
// sets sortMethod to the desired sorting string, then calls
// tableView.reloadData().

var fetchedResultsController: NSFetchedResultsController<Entity> 

    if _fetchedResultsController != nil 
        return _fetchedResultsController!
    

    let fetchRequest: NSFetchRequest<Entity> = Entity.fetchRequest()
    fetchRequest.fetchBatchSize = 20

    let sortDescriptor = NSSortDescriptor(key: sortMethod, ascending: false)

    fetchRequest.sortDescriptors = [sortDescriptor]

    let aFetchedResultsController = NSFetchedResultsController(
        fetchRequest: fetchRequest,
        managedObjectContext: managedObjectContext!,
        sectionNameKeyPath: nil,
        cacheName: "Master")

    aFetchedResultsController.delegate = self
    _fetchedResultsController = aFetchedResultsController

    do 
        try _fetchedResultsController!.performFetch()
     catch 
        // FIXME: Replace error handling stub.
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
    

    return _fetchedResultsController!


var _fetchedResultsController: NSFetchedResultsController<Card>? = nil

这适用于简单的排序,但我正在努力弄清楚如何从 A.Jam 中插入排序功能。

【问题讨论】:

仅使用谓词进行排序并且除了排序之外不使用任何其他过程是否重要? 不,我想我不必使用谓词。目标是尝试使用单个获取的结果。我不太关心这是如何实现的。获取的结果正在驱动一个 UITableView,如果这很重要的话。 我可以提供的第一个决定是将数组分成两个,对它们进行排序,然后再次加入。但仅使用一个程序来解决可能是一项有趣的任务。 有趣的 SQL 解决方案:select * from MY_COUNTS order by count>5,count desc // MY_COUNTS -- 带有值的表,用值计算它是字段。 【参考方案1】:

假设您有一个数据存储,其中已插入 10 个名为 Number 的实体。模型实体有一个名为 value 的属性,您要对其进行排序,类型为 Int32(数据存储中的值范围从 1 到 10)。

使用此函数根据您的键值拆分数据并使用NSSortDescriptor 的实例对它们进行排序:

   func fetchSortEntites(basedOn integer: Int32) -> ([Int32], [Number])

        var sortedNumberArray: [Int32] = []
        var sortedManagedObjects: [Number] = []

//        Phase 1

        let predicate1 = NSCompoundPredicate(format: "value <= %ld", integer)
        let sortDescriptor1 = NSSortDescriptor(key: "value", ascending: false)
        let fetchRequest1 = NSFetchRequest<Number>(entityName: "Number")
        fetchRequest1.predicate = predicate1
        fetchRequest1.sortDescriptors = [sortDescriptor1]

        do
            let managedObjects =  try managedObjectContext.fetch(fetchRequest1)
            for managedObject in managedObjects
                sortedNumberArray.append(managedObject.value)
                sortedManagedObjects.append(managedObject)
            
         catch let error as NSError 
            print(error.debugDescription)
            fatalError("*** Failed to fetch managed objects from the context!")
        


//        Phase 2

        let predicate2 = NSCompoundPredicate(format: "value > %ld", integer)
        let sortDescriptor2 = NSSortDescriptor(key: "value", ascending: false)
        let fetchRequest2 = NSFetchRequest<Number>(entityName: "Number")
        fetchRequest2.predicate = predicate2
        fetchRequest2.sortDescriptors = [sortDescriptor2]

        do
            let managedObjects =  try managedObjectContext.fetch(fetchRequest2)
            for managedObject in managedObjects
                sortedNumberArray.append(managedObject.value)
                sortedManagedObjects.append(managedObject)
            
         catch let error as NSError 
            print(error.debugDescription)
            fatalError("*** Failed to fetch managed objects from the context!")
        


        return (sortedNumberArray, sortedManagedObjects)
    



结果:

let myTuple = fetchSortEntites(basedOn: 5)

    for number in myTuple.0
        print (number)
    

let myTuple = fetchSortEntites(basedOn: 8)

    for number in myTuple.0
        print (number)
    

希望这有帮助!

【讨论】:

关于如何使用 NSFetchedResultsController 插入的任何提示? 我用我的 NSFetchedResultsController 和一些上下文更新了我的问题。 您好@EricSopher,我认为您不能使用NSFetchRequest 的一个实例获取结果(按您想要的顺序)。此外,您不能将多个获取请求合并到 NSFetchedResultsController 的实例中。如果您找到解决此问题的方法,请告诉我。您可以阅读this 帖子以获取更多信息。祝你好运。【参考方案2】:

您可以围绕两个 NSFetchedResultsController 创建包装器(下一个 resultsController)。 第一个 resultsController 应该在 () 阈值之后获取值。 之后,包装器可以将所需数据作为来自 requestControllers 的数据联合返回。 例如:

//Get count cells
int countCells = resultsController1.sections.first.numberOfObjects + resultsController2.sections.first.numberOfObjects;
 <...>
 //Get value for cell
int value = 0;
if (cellIndex >= <counts_in_first_resultsController>)

   value = <Get_from_the_second>;

else

  value = <Get_from_the_first>;

【讨论】:

以上是关于在 Swift 中将两个 NSPredicates 与独立的 NSSortDescriptors 结合起来的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中 NSFetchRequest 的多个 NSPredicates?

在 Swift 中将两个数组合并为一个字典

核心数据关系、NSPredicates 和 NSFetchedResultsController

在 Swift 中将两个字节的 UInt8 数组转换为 UInt16

在 Swift 3 中将结构附加到结构数组上

在 Swift 中将自定义 UITableViewCell 共享到多个 UITableViewController