もっとNSArayソート
5746 ワード
http://blog.ablepear.com/2011/12/objective-c-tuesdays-more-nsarray.html
前回はC配列とNSAray配列について話しましたが、今日はNSSortDescriptorsを使ってNSAray順序を説明し続けます。
NSArayは、比較関数を指定して並べ替えを完了することを要求しています。いくつかの簡単なタイプのNSString、NSDateのNSAray順序付けの場合、比較関数は簡単に作成され、いくつかの一般的なオブジェクトは比較関数があります。
複雑なタイプのNSArayのために並べ替えた場合、比較関数の作成はより面倒で間違いやすい。これはPersonタイプのインターフェースである。
幸いなことに、この問題を簡単に解決する方法があります。NSArayには、NSSortDescriptorオブジェクトのセットがパラメータとして受信されます。各NSSortDescriptorオブジェクトは、keyパスとソート方向(昇順または降順)を指定します。配列中のNSSortDescriptorsの順に各フィールドの優先度を決定します。KVCに慣れていないなら、keyパスに遭遇したことがないかもしれません。KVCは文字型のフィールド名でゲットできます。setオブジェクトフィールドにアクセスします。子孫対象のフィールドにアクセスするには、使用が必要です。分離されたkeyパスを使用します。KVCは面白いものが多いです。しかし、今日はNSSortDescriptorsのセットを構築するだけです。
並べ替えディスクリプタグループを使うのは簡単です。
前回はC配列とNSAray配列について話しましたが、今日はNSSortDescriptorsを使ってNSAray順序を説明し続けます。
NSArayは、比較関数を指定して並べ替えを完了することを要求しています。いくつかの簡単なタイプのNSString、NSDateのNSAray順序付けの場合、比較関数は簡単に作成され、いくつかの一般的なオブジェクトは比較関数があります。
複雑なタイプのNSArayのために並べ替えた場合、比較関数の作成はより面倒で間違いやすい。これはPersonタイプのインターフェースである。
// Person.h
@interface Person : NSObject
@property (strong) Address *address;
@property (strong) NSDate *birthdate;
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end
これはPersonが使用するAddressタイプのインターフェースです。
// Address.h
@interface Address : NSObject
@property (copy, nonatomic) NSString *street;
@property (copy, nonatomic) NSString *city;
@property (copy, nonatomic) NSString *state;
@property (copy, nonatomic) NSString *country;
@property (copy, nonatomic) NSString *postalCode;
@end
私たちはPersonのNSArayを持っています。country、lastName、first Nameによって並べ替えます。これは比較blockの実現方法です。
// sort Person objects by lastName, firstName
Person *frodo = [Person new];
[frodo setFirstName:@"Frodo"];
[frodo setLastName:@"Baggins"];
// ...
[[frodo address] setCountry:@"Shire"];
Person *bilbo = [Person new];
[bilbo setFirstName:@"Bilbo"];
[bilbo setLastName:@"Baggins"];
// ...
[[bilbo address] setCountry:@"Shire"];
Person *legolas = [Person new];
[legolas setFirstName:@"Legolas"];
[legolas setLastName:@"Greenleaf"];
// ...
[[legolas address] setCountry:@"Mirkwood"];
NSArray *people = [NSArray arrayWithObjects:frodo, bilbo, legolas, nil];
NSArray *sortedPeople = [people sortedArrayUsingComparator:^(id item1, id item2) {
Person *person1 = item1;
Person *person2 = item2;
// NSComparisonResult is a typedef for int
NSComparisonResult result = [[[person1 address] country] compare:[[person2 address] lastName]];
if (result) {
return result;
}
result = [[person1 lastName] compare:[person2 lastName]];
if (result) {
return result;
}
result = [[person1 firstName] compare:[person2 firstName]];
if (result) {
return result;
}
return NSOrderedSame; // NSOrderedSame == 0
}];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)
複数フィールド比較の汎用モードは簡単で、各フィールドを順次チェックします。比較結果が0でない場合、停止して比較結果を返します。すべてのフィールドが等しい場合は、0を返します。(または、より記述的なNSOrderedSameを使って)、子孫のオブジェクトに深く入り込んでフィールドを確認する必要がある場合は、非常に複雑になります。幸いなことに、この問題を簡単に解決する方法があります。NSArayには、NSSortDescriptorオブジェクトのセットがパラメータとして受信されます。各NSSortDescriptorオブジェクトは、keyパスとソート方向(昇順または降順)を指定します。配列中のNSSortDescriptorsの順に各フィールドの優先度を決定します。KVCに慣れていないなら、keyパスに遭遇したことがないかもしれません。KVCは文字型のフィールド名でゲットできます。setオブジェクトフィールドにアクセスします。子孫対象のフィールドにアクセスするには、使用が必要です。分離されたkeyパスを使用します。KVCは面白いものが多いです。しかし、今日はNSSortDescriptorsのセットを構築するだけです。
NSSortDescriptor *byCountry = [NSSortDescriptor sortDescriptorWithKey:@"address.country"
ascending:YES];
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES];
NSSortDescriptor *byFirstName = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:byCountry, byLastName, byFirstName, nil];
注意byCountry順序記述子はkeyパス@「address.com untry」を使います。最初にPersonオブジェクトのaddress属性値を取得し、その後addressのcountry属性値を取得します。Keyパスは任意の深さでもいいです。並べ替えディスクリプタグループを使うのは簡単です。
NSArray *sortedPeople = [people sortedArrayUsingDescriptors:sortDescriptors];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)
これは複雑な並べ替え規則を作成することで容易になり、フィールドのデフォルト比較関数に制限されなくなります。比較関数にselectorを指定してもいいです。
// specify a method to call on the lastName object
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
selector:@selector(caseInsensitiveCompare:)];
あるいは、より専門的な比較のために、NSCompratorのblockを伝えることができます。
// sort descriptor using length of last name
NSSortDescriptor *byLastNameLength = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
comparator:^(id item1, id item2) {
NSString *lastName1 = item1;
NSString *lastName2 = item2;
// cast result to NSComparisonResult so that the
// compiler infers the correct return type
return (NSComparisonResult) ([lastName1 length] - [lastName2 length]);
}];
NSSortDescriptorsで複雑な並べ替え規則を指定すると、読みやすく、書き込みやすく、メンテナンスしやすい声明コードです。ほとんどの場合、NSSortDescriptorを使うべきです。自分で比較関数を作成するのではなく、NSSortDescriptorを使うべきです。