NSSortDescriptor:同时对多个键进行自定义比较

时间:2012-10-16 15:13:30

标签: objective-c ios sorting comparison nssortdescriptor

我有一个自定义对象,其中包含基于时间段的信息,例如endCalYear,endMonth和periodLength属性,它们表示每个句点的结束及其长度。

我想创建一个基于NSSortDescriptor或其他排序方法,它结合了这三个属性,并允许同时在所有三个键上对此对象进行排序。

示例:

EndCalYear  endMonth  periodLength (months) sortOrder
2012        6         6                     1
2012        6         3                     2
2012        3         3                     3
2011        12        12                    4

排序算法将完全根据我自己的算法自行决定。

我怎么能编写这样的算法?

基于块的sortDescriptorWithKey:ascending:comparator:方法在我的视图中不起作用,因为它允许我只指定一个排序键。但是,我需要同时对所有三个键进行排序。

关于如何解决这个问题的想法或想法?

谢谢!

2 个答案:

答案 0 :(得分:19)

您可以使用块进行排序:

NSArray *sortedArray;
sortedArray = [myArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    MyObject *first = (MyObject*)a;
    MyObject *second = (MyObject*)b;

    if (first.endCalYear < second.endCalYear) {
        return NSOrderedAscending;
    }
    else if (first.endCalYear > second.endCalYear) {
        return NSOrderedDescending;
    }
    // endCalYear is the same

    if (first.endMonth < second.endMonth) {
        return NSOrderedAscending;
    }
    else if (first.endMonth > second.endMonth) {
        return NSOrderedDescending;
    }    
    // endMonth is the same

    if (first.periodLength < second.periodLength) {
        return NSOrderedAscending;
    }
    else if (first.periodLength > second.periodLength) {
        return NSOrderedDescending;
    }
    // periodLength is the same

    return NSOrderedSame;
}]

endCalYear升序排序,然后endMonth升序,最后periodLength升序排序。您可以修改它以更改顺序或切换if语句中的符号以使其降序。

对于NSFetchedResultsController,您可能想尝试其他方法:

看起来你可以pass it a list of descriptors,每列需要排序一个:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSSortDescriptor *descriptor1 = [[NSSortDescriptor alloc] initWithKey:@"endCalYear" ascending:YES];
NSSortDescriptor *descriptor2 = [[NSSortDescriptor alloc] initWithKey:@"endMonth" ascending:YES];
NSSortDescriptor *descriptor3 = [[NSSortDescriptor alloc] initWithKey:@"periodLength" ascending:YES];
NSArray *sortDescriptors = @[descriptor1, descriptor2, descriptor3];
[fetchRequest setSortDescriptors:sortDescriptors];

答案 1 :(得分:7)

用于排序的API通常能够使用NSSortDescriptors的数组,而不只是一个,所以为什么不使用它们呢?

例如,NSArray有一个名为sortedArrayUsingDescriptors:的方法(注意复数形式),它采用NSSortDescriptor个对象的数组。

所以你可以简单地写一下:

NSSortDescriptor *endCalYearSD = [NSSortDescriptor sortDescriptorWithKey:@"endCalYear" ascending:YES];
NSSortDescriptor *endMonthSD = [NSSortDescriptor sortDescriptorWithKey:@"endMonth" ascending:YES];
NSSortDescriptor *periodLenSD = [NSSortDescriptor sortDescriptorWithKey:@"periodLength" ascending:YES];

NSArray *sortedArray = [originalArray sortedArrayUsingDescriptors:@[endCalYearSD, endMonthSD, periodLenSD]];

这样,你的originalArray将首先被endCalYear排序,每个具有相同endCalYear的条目将按endMonth排序,每个具有相同endCalYear和endMonth的条目将按periodLendth排序。

对于大多数建议排序的API(包括CoreData等),您都有使用sortDescriptors数组的API,所以原则始终是相同的。


如果你真的只需要坚持一个NSSortDescriptor(你的排序算法不够灵活,不能使用基于块的比较器或NSSortDescriptors数组),你可以简单地提供一个属于自定义对象的属性,用于计算可以作为排序算法基础的值。

例如,将此类方法添加到自定义类:

-(NSUInteger)sortingIndex {
    return endCalYear*10000 + endCalMonth*100 + periodLength;
}

然后对此键/属性进行排序。这不是一个非常干净的阅读和非常漂亮的设计模式,但更好的方法是改变你的排序算法API,允许一次排序多个键,所以......


[编辑] (在基于块的API上回答您的[编辑])

我不明白为什么基于块的API sortDescriptorWithKey:ascending:comparator:不适合你。你可以在那里指定你需要的任何自定义NSComparator块,所以这个块可以告诉,给定两个对象,哪一个在另一个之前。您确定哪一个取决于您之前的方式,您只能比较endCalYearendCalYearendMonth等,因此此处对使用多个排序没有限制密钥。