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

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

 

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

  1. @interface Person : NSObject
  2.  
  3. @property (strong, nonatomic) NSString* name;
  4. @property (assign, nonatomic) int age;
  5. @property (assign, nonatomic) int income;
  6.  
  7. @end

 

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

  1. Person *A = [[Person alloc] init];
  2. A.name = @"孫無空";
  3. A.age =  33;
  4. A.income = 50;
  5.  
  6. Person *B = [[Person alloc] init];
  7. B.name = @"唐三";
  8. B.age =  43;
  9. B.income = 55;
  10.  
  11. Person *C = [[Person alloc] init];
  12. C.name = @"沙無淨";
  13. C.age =  33;
  14. C.income = 60;
  15.  
  16. NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];

 

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

  1. Person *A = [[Person alloc] init];
  2. A.name = @"孫無空";
  3. A.age =  33;
  4. A.income = 50;
  5.  
  6. Person *B = [[Person alloc] init];
  7. B.name = @"唐三";
  8. B.age =  43;
  9. B.income = 55;
  10.  
  11. Person *C = [[Person alloc] init];
  12. C.name = @"沙無淨";
  13. C.age =  33;
  14. C.income = 60;
  15.  
  16. NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];
  17.  
  18. NSComparator compare = ^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
  19.         NSString* strObj1 = (NSString*) obj1;
  20.         NSString* strObj2 = (NSString*) obj2;
  21.         NSNumber* number1 = @([strObj1 doubleValue]);
  22.         NSNumber* number2 = @([strObj2 doubleValue]);
  23.  
  24.         return [number1 compare: number2];
  25. };
  26.  
  27. NSMutableArray* arraySortCondtion = [[NSMutableArray alloc] init];
  28.     [arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_income" ascending:NO comparator: compare]];
  29. NSArray* sortArray = [unSortArray sortedArrayUsingDescriptors: arraySortCondtion];

 

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

 

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

  1. @implementation Person
  2.  
  3. - (id)valueForUndefinedKey:(NSString *)key
  4. {
  5.     if ([key isEqualToString:@"sortcond_income"]) {
  6.         return [@(self.income) stringValue];
  7.     }
  8.  
  9.     return [super valueForUndefinedKey:key];
  10. }
  11.  
  12. @end

 

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

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

  1. Person *A = [[Person alloc] init];
  2. A.name = @"孫無空";
  3. A.age =  33;
  4. A.income = 50;
  5.  
  6. Person *B = [[Person alloc] init];
  7. B.name = @"唐三";
  8. B.age =  43;
  9. B.income = 55;
  10.  
  11. Person *C = [[Person alloc] init];
  12. C.name = @"沙無淨";
  13. C.age =  33;
  14. C.income = 60;
  15.  
  16. NSMutableArray* unSortArray = [NSMutableArray arrayWithObjects:A,B,C, nil];
  17.  
  18. NSComparator compare = ^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
  19.         NSString* strObj1 = (NSString*) obj1;
  20.         NSString* strObj2 = (NSString*) obj2;
  21.         NSNumber* number1 = @([strObj1 doubleValue]);
  22.         NSNumber* number2 = @([strObj2 doubleValue]);
  23.  
  24.         return [number1 compare: number2];
  25. };
  26. NSMutableArray* arraySortCondtion = [[NSMutableArray alloc] init];
  27. [arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_age" ascending:YES comparator: compare]];
  28. [arraySortCondtion addObject: [NSSortDescriptor sortDescriptorWithKey:@"sortcond_income" ascending:NO comparator: compare]];
  29. NSArray* sortArray = [unSortArray sortedArrayUsingDescriptors: arraySortCondtion];
  30.  
  31. @end

 

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

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

 

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

  1. @implementation Person
  2.  
  3. - (id)valueForUndefinedKey:(NSString *)key
  4. {
  5.     if ([key isEqualToString:@"sortcond_income"]) {
  6.         return [@(self.income) stringValue];
  7.     }
  8.     if ([key isEqualToString:@"sortcond_age"]) {
  9.         return [@(_age) stringValue];
  10.     }
  11.     return [super valueForUndefinedKey:key];
  12. }
  13.  
  14. @end

 

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

 

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