关于iOS:如何使用自定义对象对NSMutableArray进行排序?

How do I sort an NSMutableArray with custom objects in it?

我想做的似乎很简单,但我在网上找不到任何答案。我有一个NSMutableArray的物体,假设它们是"人"的物体。我想按人的出生日期对NSMutableArray进行分类,这是NSDate的生日。

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

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

在Java中,我将使我的对象实现可比性,或者使用内嵌的自定义比较器使用Copys.S排序……在Objto-C中,您究竟是如何做到这一点的?


比较法

为对象实现比较方法:

1
2
3
4
5
- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

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

nsSortDescriptor(更好)

或者通常更好:

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

通过向数组中添加多个键,可以轻松地按多个键排序。也可以使用定制的比较器方法。查看文档。

块(闪亮的!)

由于Mac OS X 10.6和iOS 4,也有可能使用块进行排序:

1
2
3
4
5
6
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进行排序。


NSMutableArraysortUsingFunction:context:

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

这两个对象只是Person的实例。第三个对象是字符串,例如@"birthdate"。

此函数返回一个NSComparisonResult:如果PersonA.birthDate<PersonB.birthDate,则返回NSOrderedAscending。如果PersonA.birthDate大于PersonB.birthDate则返回NSOrderedDescending。最后,如果PersonA.birthDate==PersonB.birthDate,返回NSOrderedSame

这是一个粗糙的伪代码;您需要详细说明一个日期与另一个日期"更少"、"更多"或"相等"的含义(例如比较自epoch以来的秒数等):

1
2
3
4
5
6
7
8
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;
}

如果需要更紧凑的内容,可以使用三元运算符:

1
2
3
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,但是在我的代码i中,有一个异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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];

注意:"排序"数组将自动释放。


我尽力了,但这对我很管用。在一个类中,我有另一个名为"EDOCX1"(29)的类,并希望按"EDOCX1"(29)的属性进行排序。

这就像一个魅力:

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

格奥尔格·施有一个缺失的步骤吗?莱伊的第二个答案,但那就行了。

1
2
3
4
5
6
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];


1
2
3
4
5
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:打电话,应该就这么做了。

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


iOS 4块将为您节省:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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方法。它对位置进行排序,而不创建新实例。


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

1
2
3
4
5
6
7
8
9
//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];

如果您只是对一个NSNumbers数组进行排序,那么可以使用1个调用对它们进行排序:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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对象实现了比较方法,因此可以更简单地重写它:

1
2
3
4
5
6
7
8
9
 #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行:

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

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


我刚刚完成了基于定制需求的多级排序。

//对值排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    [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/multiplevelsortininginosobjectivec


我创建了一个小的类目方法库,称为LinqtoObjective,这使得这类事情更加容易。使用带有键选择器的排序方法,可以按birthDate进行排序,如下所示:

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


我在我的一些项目中使用了SortUsingFunction:::

1
2
3
4
5
6
7
8
9
10
11
12
13
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];

1
2
3
4
5
6
7
8
9
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}


分类NSMutableArray非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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];

使用nscomparator排序

如果要对自定义对象进行排序,我们需要提供NSComparator,用于比较自定义对象。块返回一个NSComparisonResult值来表示两个对象的顺序。因此,为了对整个数组进行排序,下面使用了NSComparator

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

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

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

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

1
2
3
4
5
6
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/conceptive/sortdescriptors/articles/creating.htmlhttp://www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/


Swift的协议和函数编程使得这非常容易,您只需使类符合可比较的协议,实现协议所需的方法,然后使用sorted(by:)高阶函数创建排序数组,而不需要使用可变数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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)


使用nsSortDescriptor使用自定义对象对nsmutableArray进行排序

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

像这样用于嵌套对象,

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

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


在我的例子中,我使用"sortedarrayusingcomparator"对数组进行排序。请看下面的代码。

1
2
3
4
5
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];
}];

我的目标是,

1
2
3
4
5
6
@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end

swift中的排序数组

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

1
2
3
4
5
6
class User: NSObject {
    var id: String?
    var name: String?
    var email: String?
    var createdDate: Date?
}

现在我们有了一个数组,我们需要根据createdDate进行排序,要么升序要么降序。因此,让我们添加一个用于日期比较的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    }
}

现在让我们用一个extensionArray来表示User。简单来说,让我们只为那些只有User对象的数组添加一些方法。

1
2
3
4
5
6
7
8
9
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
    }
}

升序用法

1
let sortedArray = someArray.sortUserByDate(.orderedAscending)

降序用法

1
let sortedArray = someArray.sortUserByDate(.orderedAscending)

同一订单的使用

1
let sortedArray = someArray.sortUserByDate(.orderedSame)

Above method in extension will only be accessible if the Array is of type
[User] || Array


您必须创建SortDescriptor,然后可以使用下面的SortDescriptor对NSmutableArray进行排序。

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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(@"===============================");