5

我有一个自定义对象,其中包含基于时间段的信息,例如属性 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:方法在我看来不起作用,因为它只允许我指定一个排序键。但是,我需要同时对所有三个键进行排序。

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

谢谢!

4

2 回答 2

22

您可以改为使用块进行排序:

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];
于 2012-10-16T15:20:29.143 回答
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 等),您有使用 sortDescriptor 数组的 API,因此原则始终相同。


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

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

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

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


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

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

于 2012-10-16T16:09:17.157 回答