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:方法在我的视图中不起作用,因为它允许我只指定一个排序键。 但是,我需要同时对所有三个键进行排序。

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

谢谢!

您可以使用块进行排序:

 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,您可能想尝试其他方法:

看起来您可以在其中输入一个描述符列表,每个列对应一个要排序的列:

 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]; 

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

例如, NSArray有一个名为sortedArrayUsingDescriptors:的方法sortedArrayUsingDescriptors:注意复数forms),它采用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为大多数建议排序的API(包括CoreData等)使用sortDescriptors数组,因此原则始终相同。


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

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

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

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


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

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