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

时间:2009-04-30 06:10:57

标签: ios objective-c sorting cocoa-touch nsmutablearray

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

我认为这与此方法有关:

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

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

28 个答案:

答案 0 :(得分:2266)

比较方法

要么为对象实现比较方法:

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

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

块(闪亮!)

从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进行排序。

答案 1 :(得分:106)

请参阅NSMutableArray方法sortUsingFunction:context:

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

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

此函数返回NSComparisonResult:如果NSOrderedAscending&lt; PersonA.birthDate,则返回PersonB.birthDate NSOrderedDescending。如果PersonA.birthDate&gt;它会返回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;
}

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

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

答案 2 :(得分:61)

我在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:这是按降序排序。

答案 3 :(得分:28)

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

注意:“已排序”数组将自动释放。

答案 4 :(得分:25)

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

这就像一个魅力:

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

答案 5 :(得分:19)

Georg Schölly's second answer中有一个缺失的步骤,但它工作正常。

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

答案 6 :(得分:18)

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

谢谢,它工作正常......

答案 7 :(得分:16)

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

然后,您可以使用sortedArrayUsingSelector:致电@selector(compare:),并且应该完成。

还有其他方法,但据我所知,Comparable接口没有Cocoa-equiv。使用sortedArrayUsingSelector:可能是最无痛的方法。

答案 8 :(得分:8)

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一些描述

答案 9 :(得分:7)

对于NSMutableArray,请使用sortUsingSelector方法。它在不创建新实例的情况下对其进行排序。

答案 10 :(得分:6)

您可以使用以下通用方法。它应该解决你的问题。

//Called method
-(NSMutableArray*)sortArrayList:(NSMutableArray*)arrDeviceList filterKeyName:(NSString*)sortKeyName ascending:(BOOL)isAscending{
    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:sortKeyName ascending:isAscending];
    [arrDeviceList sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    return arrDeviceList;
}

//Calling method
[self sortArrayList:arrSomeList filterKeyName:@"anything like date,name etc" ascending:YES];

答案 11 :(得分:5)

如果您只是对NSNumbers数组进行排序,则可以通过1次调用对它们进行排序:

[arrayToSort sortUsingSelector: @selector(compare:)];

这是有效的,因为数组中的对象(NSNumber对象)实现了compare方法。您可以对NSString个对象执行相同的操作,甚至可以对实现比较方法的自定义数据对象数组执行相同的操作。

这是使用比较器块的一些示例代码。它对一组字典进行排序,其中每个字典都包含一个键“sort_key”中的数字。

#define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
 ^(id obj1, id obj2) 
  {
  NSInteger value1 = [[obj1 objectForKey: SORT_KEY] intValue];
  NSInteger value2 = [[obj2 objectForKey: SORT_KEY] intValue];
  if (value1 > value2) 
{
  return (NSComparisonResult)NSOrderedDescending;
  }

  if (value1 < value2) 
{
  return (NSComparisonResult)NSOrderedAscending;
  }
    return (NSComparisonResult)NSOrderedSame;
 }];

上面的代码完成了为每个排序键获取整数值并比较它们的工作,作为如何执行此操作的说明。由于NSNumber个对象实现了一个比较方法,因此可以更简单地重写它:

 #define SORT_KEY @\"sort_key\"

[anArray sortUsingComparator: 
^(id obj1, id obj2) 
 {
  NSNumber* key1 = [obj1 objectForKey: SORT_KEY];
  NSNumber* key2 = [obj2 objectForKey: SORT_KEY];
  return [key1 compare: key2];
 }];

或比较器的主体甚至可以蒸馏到1行:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];

我倾向于选择简单的语句和许多临时变量,因为代码更容易阅读,更容易调试。编译器无论如何都会优化临时变量,因此对于一体化版本没有任何优势。

答案 12 :(得分:4)

我创建了一个名为Linq to ObjectiveC的类别方法的小型库,这使得这类事情变得更加容易。使用密钥选择器sort方法,您可以按birthDate排序,如下所示:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]

答案 13 :(得分:4)

我刚刚根据自定义要求进行了多级排序。

//对值进行排序

    [arrItem sortUsingComparator:^NSComparisonResult (id a, id b){

    ItemDetail * itemA = (ItemDetail*)a;
    ItemDetail* itemB =(ItemDetail*)b;

    //item price are same
    if (itemA.m_price.m_selling== itemB.m_price.m_selling) {

        NSComparisonResult result=  [itemA.m_itemName compare:itemB.m_itemName];

        //if item names are same, then monogramminginfo has to come before the non monograme item
        if (result==NSOrderedSame) {

            if (itemA.m_monogrammingInfo) {
                return NSOrderedAscending;
            }else{
                return NSOrderedDescending;
            }
        }
        return result;
    }

    //asscending order
    return itemA.m_price.m_selling > itemB.m_price.m_selling;
}];

https://sites.google.com/site/greateindiaclub/mobil-apps/ios/multilevelsortinginiosobjectivec

答案 14 :(得分:4)

我在一些项目中使用了 sortUsingFunction ::

int SortPlays(id a, id b, void* context)
{
    Play* p1 = a;
    Play* p2 = b;
    if (p1.score<p2.score) 
        return NSOrderedDescending;
    else if (p1.score>p2.score) 
        return NSOrderedAscending;
    return NSOrderedSame;
}

...
[validPlays sortUsingFunction:SortPlays context:nil];

答案 15 :(得分:3)

-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}

答案 16 :(得分:2)

排序NSMutableArray非常简单:

NSMutableArray *arrayToFilter =
     [[NSMutableArray arrayWithObjects:@"Photoshop",
                                       @"Flex",
                                       @"AIR",
                                       @"Flash",
                                       @"Acrobat", nil] autorelease];

NSMutableArray *productsToRemove = [[NSMutableArray array] autorelease];

for (NSString *products in arrayToFilter) {
    if (fliterText &&
        [products rangeOfString:fliterText
                        options:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)

        [productsToRemove addObject:products];
}
[arrayToFilter removeObjectsInArray:productsToRemove];

答案 17 :(得分:1)

使用NSComparator排序

如果我们想要对自定义对象进行排序,我们需要提供NSComparator,用于比较自定义对象。该块返回NSComparisonResult值以表示两个对象的排序。因此,为了对整个数组NSComparator进行排序,请按以下方式使用。

NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
    return [e1.firstname compare:e2.firstname];    
}];

使用NSSortDescriptor排序
作为一个例子,我们假设我们有一个包含自定义类实例的数组,Employee具有属性firstname,lastname和age。以下示例说明如何创建可用于按年龄键按升序对数组内容进行排序的NSSortDescriptor。

NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

使用自定义比较排序
名称是字符串,当您对字符串进行排序以呈现给用户时,您应始终使用本地化比较。通常,您还希望执行不区分大小写的比较。这里有一个示例(localizedStandardCompare :),按姓氏和名字排序数组。

NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]
              initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];

有关参考和详细讨论,请参阅: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
http://www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/

答案 18 :(得分:1)

Swift的协议和函数式编程使您只需使您的类符合Comparable协议,实现协议所需的方法,然后使用sorted(by :)高阶函数创建顺序排列的数组,不需要使用可变数组。

return Promise.map(arrayOfFiles, file => {  
  return callExternalLib(file).then((data) => {
    file.data = data;
    return file;
  }
}, { concurrency: maxOfAsync }) 

答案 19 :(得分:1)

使用NSSortDescriptor为NSMutableArray排序自定义对象

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

答案 20 :(得分:1)

快速版本:5.1

如果您有一个自定义的结构或类,并希望对其进行任意排序,则应使用对指定字段进行排序的结尾闭包来调用sort()。这是一个使用自定义结构数组对特定属性进行排序的示例:

    struct User {
        var firstName: String
    }

    var users = [
        User(firstName: "Jemima"),
        User(firstName: "Peter"),
        User(firstName: "David"),
        User(firstName: "Kelly"),
        User(firstName: "Isabella")
    ]

    users.sort {
        $0.firstName < $1.firstName
    }

如果要返回排序后的数组而不是对其进行排序,请使用sorted(),如下所示:

    let sortedUsers = users.sorted {
        $0.firstName < $1.firstName
    }

答案 21 :(得分:1)

  let sortedUsers = users.sorted {
    $0.firstName < $1.firstName
 }

答案 22 :(得分:0)

在我的情况下,我使用&#34; sortedArrayUsingComparator&#34;排序数组。请看下面的代码。

contactArray = [[NSArray arrayWithArray:[contactSet allObjects]] sortedArrayUsingComparator:^NSComparisonResult(ContactListData *obj1, ContactListData *obj2) {
    NSString *obj1Str = [NSString stringWithFormat:@"%@ %@",obj1.contactName,obj1.contactSurname];
    NSString *obj2Str = [NSString stringWithFormat:@"%@ %@",obj2.contactName,obj2.contactSurname];
    return [obj1Str compare:obj2Str];
}];

我的目标也是,

@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end

答案 23 :(得分:0)

在Swift中排序数组

对于Swifty以下人员是一种非常干净的技术,可以实现全球的上述目标。让我们有一个示例自定义类User,它有一些属性。

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
}

现在我们有一个数组,我们需要在createdDate的基础上按升序和/或降序排序。所以我们添加一个日期比较函数。

class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
    func checkForOrder(_ otherUser: User, _ order: ComparisonResult) -> Bool {
        if let myCreatedDate = self.createdDate, let othersCreatedDate = otherUser.createdDate {
            //This line will compare both date with the order that has been passed.
            return myCreatedDate.compare(othersCreatedDate) == order
        }
        return false
    }
}

现在,让extension ArrayUser。简单来说,我们只为那些只有User个对象的Array添加一些方法。

extension Array where Element: User {
    //This method only takes an order type. i.e ComparisonResult.orderedAscending
    func sortUserByDate(_ order: ComparisonResult) -> [User] {
        let sortedArray = self.sorted { (user1, user2) -> Bool in
            return user1.checkForOrder(user2, order)
        }
        return sortedArray
    }
}

升序用法

let sortedArray = someArray.sortUserByDate(.orderedAscending)

降序用法

let sortedArray = someArray.sortUserByDate(.orderedAscending)

使用相同订单

let sortedArray = someArray.sortUserByDate(.orderedSame)
  

extension中的上述方法仅在Array属于类型时才可访问   [User] || Array<User>

答案 24 :(得分:0)

您必须创建sortDescriptor,然后您可以使用sortDescriptor对nsmutablearray进行排序。

 let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
 let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
 print(array)

答案 25 :(得分:0)

像这样用于嵌套对象,

NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];

lastRoute是一个对象,该对象持有to对象,而to object持有姓氏字符串值。

答案 26 :(得分:-1)

NSSortDescriptor  *sort = [[NSSortDescriptor alloc] initWithKey:@"_strPrice"
                                                 ascending:sortFlag selector:@selector(localizedStandardCompare:)] ;

答案 27 :(得分:-3)

NSMutableArray *stockHoldingCompanies = [NSMutableArray arrayWithObjects:fortune1stock,fortune2stock,fortune3stock,fortune4stock,fortune5stock,fortune6stock , nil];

NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:@"companyName" ascending:NO];

[stockHoldingCompanies sortUsingDescriptors:[NSArray arrayWithObject:sortOrder]];

NSEnumerator *enumerator = [stockHoldingCompanies objectEnumerator];

ForeignStockHolding *stockHoldingCompany;

NSLog(@"Fortune 6 companies sorted by Company Name");

    while (stockHoldingCompany = [enumerator nextObject]) {
        NSLog(@"===============================");
        NSLog(@"CompanyName:%@",stockHoldingCompany.companyName);
        NSLog(@"Purchase Share Price:%.2f",stockHoldingCompany.purchaseSharePrice);
        NSLog(@"Current Share Price: %.2f",stockHoldingCompany.currentSharePrice);
        NSLog(@"Number of Shares: %i",stockHoldingCompany.numberOfShares);
        NSLog(@"Cost in Dollars: %.2f",[stockHoldingCompany costInDollars]);
        NSLog(@"Value in Dollars : %.2f",[stockHoldingCompany valueInDollars]);
    }
    NSLog(@"===============================");