如何在 NSArray 中使用多個排序條件來排序?

相信如果你是 iOS 的程式設計師,一定都有使用過 NSArray 這個非常基本的元件,而大多數的程式設計師,在使用 NSArray 時,都會使用到排序的功能,而排序不外乎就是使用「sortedArrayUsingComparator」和「NSComparisonResult」的且合方式,但如果有多個條件要同時排序,這可就頭大了!幸好 iOS 提供了另一種陣列排序的方式,叫做「sortedArrayUsingDescriptors」,怎麼用呢?先別急,我們先看範例。

 

舉例來說,我們有一個物件,叫做「Person」,宣告如下:

   
@interface Person : NSObject
 
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) int age;
@property (assign, nonatomic) int income;
 
@end

 

而接下來,我們分別宣告三個人,並把他們三個人加入陣列中,如下

   
Person *A = [[Person alloc] init];
A.name = @"孫無空";
A.age =  33;
A.income = 50;
 
Person *B = [[Person alloc] init];
B.name = @"唐三";
B.age =  43;
B.income = 55;
 
Person *C = [[Person alloc] init];
C.name = @"沙無淨";
C.age =  33;
C.income = 60;
 
NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];

 

假設,我們要以「收入(income)」做為排序,收入愈高的排愈前面,怎麼排呢?很簡單,只要寫這樣~~

   
Person *A = [[Person alloc] init];
A.name = @"孫無空";
A.age =  33;
A.income = 50;
 
Person *B = [[Person alloc] init];
B.name = @"唐三";
B.age =  43;
B.income = 55;
 
Person *C = [[Person alloc] init];
C.name = @"沙無淨";
C.age =  33;
C.income = 60;
 
NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];
 
NSComparator compare = ^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSString* strObj1 = (NSString*) obj1;
        NSString* strObj2 = (NSString*) obj2;
        NSNumber* number1 = @([strObj1 doubleValue]);
        NSNumber* number2 = @([strObj2 doubleValue]);
 
        return [number1 compare: number2];
};
 
NSMutableArray* arraySortCondtion = [[NSMutableArray alloc] init];
    [arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_income" ascending:NO comparator: compare]];
NSArray* sortArray = [unSortArray sortedArrayUsingDescriptors: arraySortCondtion];

 

其中最重要的就是「sortedArrayUsingDescriptors」了,首先你要定義 Sort 的 Key,由於我們是依收入排序,所以我們定義「sortcond_income」;再來,由於我們是採用降冪排序,也就是收入高的在前面,所以在這裡「ascending」要設為 NO,最後就是你自己定義的 compare 函式了。

 

但是,代誌沒有你我想的這麼簡單,如果真的這樣排,會出錯,為什麼呢?因為在「Person」物件不認識「sortcond_income」這個 key 呀!所以我們要改寫一下 Person 物件,加入了「valueForUndefinedKey」。如此一來,當我們去抓 sortcond_income 時,就會把 income 轉成 NSString 來匯出了。

   
@implementation Person
 
- (id)valueForUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"sortcond_income"]) {
        return [@(self.income) stringValue];
    }
 
    return [super valueForUndefinedKey:key];
}
 
@end

 

如果你依以上的排序方式實做一次,那麼最後的結果依序就是「沙無淨」、「唐三」、「孫無空」了。

但是,如果我們想要一次排多個條件呢?比如說這次採用「年齡升冪」排序,若是同樣的年齡,再採用「收入降冪」排序,那麼將如何寫呢?很簡單~~

   
Person *A = [[Person alloc] init];
A.name = @"孫無空";
A.age =  33;
A.income = 50;
 
Person *B = [[Person alloc] init];
B.name = @"唐三";
B.age =  43;
B.income = 55;
 
Person *C = [[Person alloc] init];
C.name = @"沙無淨";
C.age =  33;
C.income = 60;
 
NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];
 
NSComparator compare = ^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        NSString* strObj1 = (NSString*) obj1;
        NSString* strObj2 = (NSString*) obj2;
        NSNumber* number1 = @([strObj1 doubleValue]);
        NSNumber* number2 = @([strObj2 doubleValue]);
 
        return [number1 compare: number2];
};
NSMutableArray* arraySortCondtion = [[NSMutableArray alloc] init];
[arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_age" ascending:YES comparator: compare]];
[arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_income" ascending:NO comparator: compare]];
NSArray* sortArray = [unSortArray sortedArrayUsingDescriptors: arraySortCondtion];
 
@end

 

看到沒有,我們只不過多加了一行程式碼就完成了,而這行程式碼如下

   
[arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_age" ascending:YES comparator: compare]];

 

同樣的,一樣要在 Person 物件定義「sortcond_age」喔

   
@implementation Person
 
- (id)valueForUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"sortcond_income"]) {
        return [@(self.income) stringValue];
    }
    if ([key isEqualToString:@"sortcond_age"]) {
        return [@(_age) stringValue];
    }
    return [super valueForUndefinedKey:key];
}
 
@end

 

如果你依以上的排序方式實做一次,那麼最後的結果依序就是「沙無淨」、「孫無空」、「唐三」了。為什麼呢?因為「沙無淨」、「孫無空」的「年齡 (age) 」是一樣的,所以會依第二個條件「收入(income)」來做降冪排序,「沙無淨」的收入大於「孫無空」,所以自然就擺在「孫無空」 前面了,而可憐的「唐三」因為「年齡 (age) 」最大,所以被丟到最後面啦~~

 

以上就是 iOS 的陣列排序介紹,是以 Objective-C 做描述,讀者有興趣可以自行轉成 Swift,希望您使用起來能夠輕鬆愉快囉~

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments