如何对带有自定义对象的 NSMutableArray 进行排序?

我想做的事情看起来很简单,但是我在网上找不到任何答案。我有一个NSMutableArray对象,可以说它们是 “Person” 对象。我想按 Person.birthDate 排序NSMutableArray ,它是一个NSDate

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

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

在 Java 中,我可以使我的对象实现 Comparable,或者将 Collections.sort 与内联自定义比较器一起使用... 实际上,您如何在 Objective-C 中做到这一点?

答案

比较方法

您可以为对象实现比较方法:

- (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 ,在本例中,我只是返回了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:按降序排列。

从 iOS 4 开始,您还可以使用块进行排序。

对于此特定示例,我假设您数组中的对象具有 “position” 方法,该方法返回NSInteger

NSArray *arrayToSort = where ever you get the array from... ;
NSComparisonResult (^sortBlock)(id, id) = ^(id obj1, id obj2) 
{
    if ([obj1 position] > [obj2 position]) 
    { 
        return (NSComparisonResult)NSOrderedDescending;
    }
    if ([obj1 position] < [obj2 position]) 
    {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
};
NSArray *sorted = [arrayToSort sortedArrayUsingComparator:sortBlock];

注意:“排序的” 数组将自动发布。

我尝试了所有,但这对我有用。在一个类中,我有另一个名为 “ crimeScene ” 的类,并希望按 “ crimeScene ” 的属性进行排序。

这就像一个魅力:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];

GeorgSchölly 的第二个答案中缺少一个步骤,但随后效果很好。

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

谢谢,一切正常...

您的Person对象需要实现一个方法,例如compare:它接受另一个Person对象,并根据这两个对象之间的关系返回NSComparisonResult

然后,您可以使用@selector(compare:)调用sortedArrayUsingSelector: @selector(compare:)它应该完成。

还有其他方法,但据我所知, Comparable接口还没有可可当量。使用sortedArrayUsingSelector:可能是最sortedArrayUsingSelector:方法。

iOS 4 块将为您节省:)

featuresArray = [[unsortedFeaturesArray sortedArrayUsingComparator: ^(id a, id b)  
{
    DMSeatFeature *first = ( DMSeatFeature* ) a;
    DMSeatFeature *second = ( DMSeatFeature* ) b;

    if ( first.quality == second.quality )
        return NSOrderedSame;
    else
    {
        if ( eSeatQualityGreen  == m_seatQuality || eSeatQualityYellowGreen == m_seatQuality || eSeatQualityDefault  == m_seatQuality )
        {
            if ( first.quality < second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        }
        else // eSeatQualityRed || eSeatQualityYellow
        {
            if ( first.quality > second.quality )
                return NSOrderedAscending;
            else
                return NSOrderedDescending;
        } 
    }
}] retain];

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html有点描述

对于NSMutableArray ,请使用sortUsingSelector方法。它对它进行排序,而不创建新实例。