协慌网

登录 贡献 社区

如何使用自定义对象对 NSMutableArray 进行排序?

我想做的事情似乎很简单,但我在网上找不到任何答案。我有一个NSMutableArray对象,让我们说它们是'Person' 对象。我想通过 Person.birthDate 对NSMutableArray进行排序,这是一个NSDate

我认为这与这个方法有关:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];

在 Java 中,我会使我的对象实现 Comparable,或者使用带有内联自定义比较器的 Collections.sort ... 你到底如何在 Objective-C 中执行此操作?

答案

比较方法

要么为对象实现 compare 方法:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor(更好)

或者通常更好:

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];

通过向阵列添加多个键,您可以轻松地按多个键进行排序。也可以使用自定义比较器方法。看看文档

块(有光泽!)

从 Mac OS X 10.6 和 iOS 4 开始,还可以使用块进行排序:

NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person*)a birthDate];
    NSDate *second = [(Person*)b birthDate];
    return [first compare:second];
}];

性能

通常, -compare:和基于块的方法比使用NSSortDescriptor要快得多,因为后者依赖于 KVC。 NSSortDescriptor方法的主要优点是它提供了一种使用数据而不是代码定义排序顺序的方法,这使得设置变得容易,因此用户可以通过单击标题行对NSTableView进行排序。

请参阅NSMutableArray方法sortUsingFunction:context:

您需要设置一个比较函数,它接受两个对象(类型为Person ,因为您要比较两个Person对象)和一个上下文参数。

这两个对象只是Person实例。第三个对象是一个字符串,例如 @“birthDate”。

该函数返回一个NSComparisonResult :它返回NSOrderedAscending如果PersonA.birthDate < PersonB.birthDate 。它将返回NSOrderedDescending如果PersonA.birthDate > PersonB.birthDate 。最后,它会返回NSOrderedSame如果PersonA.birthDate == PersonB.birthDate

这是粗糙的伪代码; 你需要充实一个日期对于另一个日期 “更少”,“更多” 或 “相等” 意味着什么(例如比较秒 - 自 - 纪元等):

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  if ([firstPerson birthDate] < [secondPerson birthDate])
    return NSOrderedAscending;
  else if ([firstPerson birthDate] > [secondPerson birthDate])
    return NSOrderedDescending;
  else 
    return NSOrderedSame;
}

如果你想要更紧凑的东西,你可以使用三元运算符:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}

如果你这么做的话,内联也许可以加快一点。

我在 iOS 4 中使用块来完成此操作。不得不将我的数组元素从 id 转换为我的类类型。在这种情况下,它是一个名为 Score 的类,其中包含一个名为 points 的属性。

如果数组的元素不是正确的类型,你还需要决定该怎么做,对于这个例子我刚刚返回NSOrderedSame ,但是在我的代码中我虽然是异常。

NSArray *sorted = [_scores sortedArrayUsingComparator:^(id obj1, id obj2){
    if ([obj1 isKindOfClass:[Score class]] && [obj2 isKindOfClass:[Score class]]) {
        Score *s1 = obj1;
        Score *s2 = obj2;

        if (s1.points > s2.points) {
            return (NSComparisonResult)NSOrderedAscending;
        } else if (s1.points < s2.points) {
            return (NSComparisonResult)NSOrderedDescending;
        }
    }

    // TODO: default is the same?
    return (NSComparisonResult)NSOrderedSame;
}];

return sorted;

PS:这是按降序排序。