1309

我想做的事情似乎很简单,但我在网上找不到任何答案。我有一个NSMutableArray对象,假设它们是“人”对象。我想NSMutableArray按 Person.birthDate 排序,这是一个NSDate.

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

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

在 Java 中,我会让我的对象实现 Comparable,或者将 Collections.sort 与内联自定义比较器一起使用……你到底是如何在 Objective-C 中做到这一点的?

4

27 回答 27

2333

比较方法

要么为你的对象实现一个比较方法:

- (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(Person *a, Person *b) {
    return [a.birthDate compare:b.birthDate];
}];

表现

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

于 2009-04-30T06:25:03.067 回答
112

NSMutableArray方法sortUsingFunction:context:

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

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

这个函数返回一个NSComparisonResult: 它返回NSOrderedAscendingif PersonA.birthDate< PersonB.birthDateNSOrderedDescending如果PersonA.birthDate>它将返回PersonB.birthDateNSOrderedSame最后,如果PersonA.birthDate==它将返回PersonB.birthDate

这是粗略的伪代码;您将需要充实一个日期“更少”、“更多”或“等于”另一个日期的含义(例如比较 seconds-since-epoch 等):

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;
}

如果您经常这样做,内联可能会加快一点速度。

于 2009-04-30T06:24:09.310 回答
65

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

于 2011-03-24T22:15:46.667 回答
30

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

这就像一个魅力:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
于 2010-11-22T10:51:34.753 回答
30

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

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

于 2011-03-16T18:46:26.247 回答
27

Georg Schölly 的第二个答案中缺少一个步骤,但它工作正常。

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

// 添加 's' 因为复制和粘贴时浪费了时间,并且在 sortedArrayUsingDescriptors 中没有 's' 失败

于 2009-05-06T16:25:51.213 回答
20
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];

谢谢,它工作正常...

于 2011-03-30T05:54:36.710 回答
18

你的Person对象需要实现一个方法,说compare:哪个接受另一个Person对象,然后NSComparisonResult根据两个对象之间的关系返回。

然后你会打电话给sortedArrayUsingSelector:@selector(compare:),它应该完成。

还有其他方法,但据我所知,没有可可等效的Comparable界面。使用sortedArrayUsingSelector:可能是最轻松的方法。

于 2009-04-30T06:20:00.393 回答
11

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一点说明

于 2011-04-14T09:03:10.037 回答
9

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

于 2010-11-09T13:47:20.733 回答
8

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

//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];
于 2015-04-16T09:17:48.890 回答
7

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

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

这是因为数组中的对象(NSNumber对象)实现了比较方法。您可以对对象执行相同的操作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对象实现了 compare 方法,因此可以更简单地重写它:

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

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

于 2012-12-06T07:51:07.733 回答
7

您使用 NSSortDescriptor 对带有自定义对象的 NSMutableArray 进行排序

 NSSortDescriptor *sortingDescriptor;
 sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                       ascending:YES];
 NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
于 2018-06-14T09:21:27.343 回答
6

我在一些项目中使用了 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];
于 2011-05-08T23:27:45.850 回答
6
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}
于 2013-01-10T15:37:39.493 回答
6

我创建了一个小的类别方法库,称为Linq to ObjectiveC,它使这类事情变得更容易。使用带有键选择器的sortbirthDate方法,您可以按如下方式进行排序:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]
于 2013-03-06T08:29:13.000 回答
6

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

//对值进行排序

    [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

于 2013-06-04T10:09:00.023 回答
4

排序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];
于 2011-09-06T09:44:57.473 回答
4

使用 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 可用于按年龄键对数组内容进行升序排序。

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/

于 2016-06-16T09:32:26.570 回答
3

Swift 的协议和函数式编程让这变得非常容易,您只需让您的类符合 Comparable 协议,实现协议所需的方法,然后使用 sorted(by: ) 高阶函数创建排序数组,无需使用顺便说一句,可变数组。

class Person: Comparable {
    var birthDate: NSDate?
    let name: String

    init(name: String) {
        self.name = name
    }

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate === rhs.birthDate || lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedSame
    }

    static func <(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedAscending
    }

    static func >(lhs: Person, rhs: Person) -> Bool {
        return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedDescending
    }

}

let p1 = Person(name: "Sasha")
p1.birthDate = NSDate() 

let p2 = Person(name: "James")
p2.birthDate = NSDate()//he is older by miliseconds

if p1 == p2 {
    print("they are the same") //they are not
}

let persons = [p1, p2]

//sort the array based on who is older
let sortedPersons = persons.sorted(by: {$0 > $1})

//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)
于 2017-03-27T22:14:35.977 回答
2

就我而言,我使用“sortedArrayUsingComparator”对数组进行排序。看看下面的代码。

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
于 2016-12-13T12:22:14.897 回答
2

您必须创建 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)
于 2018-04-12T10:32:49.360 回答
1

在 Swift 中排序数组


For SwiftyPerson below 是一种非常干净的技术,可以在全球范围内实现上述目标。让我们有一个示例自定义类,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一个Arrayfor User。简而言之,让我们只为那些只有User对象的数组添加一些方法。

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>

于 2018-03-24T21:20:34.763 回答
1

像这样用于嵌套对象,

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

lastRoute 是一个对象,该对象保存 to 对象,该对象保存 lastname 字符串值。

于 2019-04-24T13:19:50.337 回答
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
    }
于 2020-02-20T13:23:44.967 回答
1
  let sortedUsers = users.sorted {
    $0.firstName < $1.firstName
 }
于 2020-03-16T13:31:05.877 回答
-2
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(@"===============================");
于 2013-10-29T01:28:27.653 回答