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

Ale*_*exR 5 sorting comparison objective-c nssortdescriptor ios

我有一个自定义对象,其中包含基于时间段的信息,例如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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

谢谢!

Nat*_*usa 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;
}]
Run Code Online (Sandbox Code Playgroud)

这通过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];
Run Code Online (Sandbox Code Playgroud)


Ali*_*are 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]];
Run Code Online (Sandbox Code Playgroud)

这样,您originalArray将首先按endCalYear排序,每个具有相同endCalYear的条目将按endMonth排序,并且具有相同endCalYear和endMonth的每个条目将按periodLendth排序.

您有API为大多数建议排序的API(包括CoreData等)使用sortDescriptors数组,因此原则始终相同.


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

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

-(NSUInteger)sortingIndex {
    return endCalYear*10000 + endCalMonth*100 + periodLength;
}
Run Code Online (Sandbox Code Playgroud)

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


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

我不明白为什么基于块的API sortDescriptorWithKey:ascending:comparator:不适合你.你可以NSComparator在那里指定你需要的任何自定义块,所以这个块可以告诉,给定两个对象,哪一个在另一个之前.你确定哪一个是前一个是你的样子,你只能比较endCalYear,还是endCalYearendMonth因此没有限制此有关使用多个键排序等等.